単体テストにパケットキャプチャを組み込む α版

NUnitVisual Studioのテストプロジェクトなどの単体テスト(Unit Test)で、ネットワークのやり取りをテスト条件に加えたかったので、
http://d.hatena.ne.jp/TripleX/20070527
http://msdn2.microsoft.com/ja-jp/library/bew39x2a(VS.80).aspx
このあたりを参考にして、パケットキャプチャを作ってみた。
まだ、きちんとテストしていないので使うときはat your own riskで。



using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace PacketUnit
{
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 8192;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
//public StringBuilder sb = new StringBuilder();

public List bytesReceived = new List();

public bool closed = false;
}

public class CaptureCore
{
private string ipAddress;
public string IpAddress
{
get { return this.ipAddress; }
set { this.ipAddress = value; }
}

private byte[] bytesCaputured;
public byte[] BytesCaputured
{
get { return this.bytesCaputured; }
}

private StateObject state;
private Socket socket;

public void StartCapture()
{
bytesCaputured = null;

socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
socket.Bind(new IPEndPoint(IPAddress.Parse(this.IpAddress),0));

byte[] byTrue = new byte[4] { 1, 0, 0, 0 };//この設定値の理由は不明
byte[] byOut = new byte[4];//この設定値の理由は不明

socket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.HeaderIncluded,
true);

socket.IOControl(IOControlCode.ReceiveAll,
byTrue,
byOut);

state = new StateObject();
state.workSocket = socket;
socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);

}

public void EndCapture()
{
state.closed = true;
bytesCaputured = state.bytesReceived.ToArray();
}

private static void ReceiveCallback(IAsyncResult ar)
{
StateObject currentstate = (StateObject)ar.AsyncState;
Socket currentsocket = currentstate.workSocket;

int bytesRead = currentsocket.EndReceive(ar);

if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
//state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
for(int i = 0; i < bytesRead; i++)
{
currentstate.bytesReceived.Add(currentstate.buffer[i]);
}

// Get the rest of the data.
if (!currentstate.closed)
{
currentsocket.BeginReceive(currentstate.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), currentstate);
}
}


}

}
}

使い方


capture.StartCapture();
//
SomeNetworkOperations();
//
capture.EndCapture();
string captured = ToHexString(capture.BytesCaputured);
string expected = ToHexString(Encoding.ASCII.GetBytes("text to test"));
Assert.IsTrue(captured.IndexOf(expected) > -1);


private string ToHexString(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
{
sb.Append(Convert.ToString(b, 16));
}
return sb.ToString();
}