すべてのニーズに対して SocketAsyncEventArgs インスタンスを 1 つだけ使用します。各リクエスト間でバッファーをリセットするだけです (新しい Byte[] に設定することにより)。
接続してソケットへの参照を取得したら、次のようにリッスンを開始します。
public void StartListening(SocketAsyncEventArgs e)
{
ResetBuffer(e);
e.Completed += SocketReceive;
socket.ReceiveAsync(e);
}
バッファをリセットするヘルパー関数があります:
private void ResetBuffer(SocketAsyncEventArgs e)
{
var buffer = new Byte[SocketBufferSize];
e.SetBuffer(buffer, 0, SocketBufferSize);
}
次のようにデータを処理します。
private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
ProcessData(e.Buffer, 0, e.BytesTransferred);
ResetBuffer(e);
socket.ReceiveAsync(e);
}
ProcessData では、必要に応じてバイト配列を使用してデータを取得できます。次のように、これを使用して MemoryStream を作成し、それをクラスに逆シリアル化します (ClientPacket と同様)。
private void ProcessData(Byte[] data, Int32 count)
{
using (var stream = new MemoryStream(data, 0, count))
{
var serializer = new XmlSerializer(typeof(ClientPacket));
var packet = serializer.Deserialize(stream);
// Do something with the packet
}
}
最後の質問について。フレームワークは、基礎となる TCP プロトコルなどに関するすべてを処理するため、処理するデータがあるときはいつでも呼び出されるイベント ハンドラーに依存できます。e.BytesTransferred 値を使用して、実際に受信したデータの量を示します。これは、バッファー サイズ (私のコードでは SocketBufferSize) よりも小さい場合がありますが、超えることはありません。メッセージがバッファ サイズよりも大きい場合、TCP インフラストラクチャはメッセージをバッファリングし、SocketBufferSize に基づいてチャンクで送信します (チャンクごとに 1 回イベントを発生させます)。これが問題になる場合は、メッセージの大部分が 1 つのチャンクで受信されるまで、SocketBufferSize を増やしてください。
チャンキングの欠点は、メッセージがインフラストラクチャによってマージされる可能性があることです。つまり、最初のメッセージがいつ終了したかを知る方法が必要になる場合があります。典型的なアプローチには、メッセージの長さを示す 4 バイトの整数をメッセージの前に付けることが含まれます。必要に応じて、さらに詳しく説明できます。
それが役立つことを願っています。