効果的に非同期ソケットを実装するには、各ソケットに1つ以上のSocketAsyncEventArgsが必要です。各SocketAsyncEventArgsのbyte []バッファにも問題があります。要するに、マネージネイティブのトランジション(送信/受信)が発生するたびにバイトバッファが固定されます。必要に応じてSocketAsyncEventArgsとバイトバッファを割り当てると、フラグメンテーションや固定されたメモリをコンパクトにすることができないため、多くのクライアントでOutOfMemoryExceptionsを実行できます。
これを処理する最善の方法は、アプリケーションが最初に起動されたときに多数のバイトとSocketAsyncEventArgsを割り当てるSocketBufferPoolクラスを作成することです。この方法で固定されたメモリは連続しています。次に、必要に応じてプールからバッファを単に再利用します。
実際には、リソースの配布を管理するために、SocketAsyncEventArgsとSocketBufferPoolクラスの周りにラッパークラスを作成するのが最適です。例として
は、ここでBeginReceiveメソッドのコードである:
private void BeginReceive(Socket socket)
{
Contract.Requires(socket != null, "socket");
SocketEventArgs e = SocketBufferPool.Instance.Alloc();
e.Socket = socket;
e.Completed += new EventHandler<SocketEventArgs>(this.HandleIOCompleted);
if (!socket.ReceiveAsync(e.AsyncEventArgs)) {
this.HandleIOCompleted(null, e);
}
}
およびここでHandleIOCompleted方法であって、上記のコードは引き上げるTcpSocketクラスに含まれる
private void HandleIOCompleted(object sender, SocketEventArgs e)
{
e.Completed -= this.HandleIOCompleted;
bool closed = false;
lock (this.sequenceLock) {
e.SequenceNumber = this.sequenceNumber++;
}
switch (e.LastOperation) {
case SocketAsyncOperation.Send:
case SocketAsyncOperation.SendPackets:
case SocketAsyncOperation.SendTo:
if (e.SocketError == SocketError.Success) {
this.OnDataSent(e);
}
break;
case SocketAsyncOperation.Receive:
case SocketAsyncOperation.ReceiveFrom:
case SocketAsyncOperation.ReceiveMessageFrom:
if ((e.BytesTransferred > 0) && (e.SocketError == SocketError.Success)) {
this.BeginReceive(e.Socket);
if (this.ReceiveTimeout > 0) {
this.SetReceiveTimeout(e.Socket);
}
} else {
closed = true;
}
if (e.SocketError == SocketError.Success) {
this.OnDataReceived(e);
}
break;
case SocketAsyncOperation.Disconnect:
closed = true;
break;
case SocketAsyncOperation.Accept:
case SocketAsyncOperation.Connect:
case SocketAsyncOperation.None:
break;
}
if (closed) {
this.HandleSocketClosed(e.Socket);
}
SocketBufferPool.Instance.Free(e);
}
DataReceived & DataSentイベント。注目すべきは、SocketAsyncOperation.ReceiveMessageFrom:block;ソケットにエラーがない場合、すぐに別のBeginReceive()が起動し、プールから別のSocketEventArgsを割り当てます。
もう1つ重要なのは、HandleIOCompleteメソッドで設定されたSocketEventArgs SequenceNumberプロパティです。非同期要求はキューに入れられた順に完了しますが、他のスレッド競合条件の影響を受けます。コードがBeginReceiveを呼び出す前にDataReceivedイベントを呼び出すため、BeginReceiveを呼び出した後でイベントをラーニングする前に元のIOCPを処理するスレッドがブロックされ、DataReceivedイベントを最初に発生させる新しいスレッドで2番目の非同期受信が完了する可能性があります。これは非常にまれなケースですが、SequenceNumberプロパティを使用すると、データが正しい順序で処理されるようにすることができます。
その他の注意すべき領域は、非同期送信です。多くの場合、非同期送信要求は同期的に完了します(呼び出しが同期した場合、SendAsyncはfalseを返します)、パフォーマンスが大幅に低下することがあります。 IOCPで返される非同期コールの追加オーバーヘッドは、実際には単純に同期呼び出しを使用する場合よりもパフォーマンスが悪化する可能性があります。非同期呼び出しでは、2つのカーネル呼び出しとヒープ割り当てが必要ですが、同期呼び出しはスタック上で発生します。
これはあなたのコードでは、 ビル