Net.Sockets.TcpListener を使用する場合、別のスレッドで着信接続 (.AcceptSocket) を処理する最良の方法は何ですか?
アイデアは、新しい着信接続が受け入れられたときに新しいスレッドを開始することですが、tcplistener はその後の着信接続に対して引き続き使用できます (そして、新しい着信接続ごとに新しいスレッドが作成されます)。接続を開始したクライアントとのすべての通信と終了は、スレッドで処理されます。
VB.NET コードの C# の例を歓迎します。
Net.Sockets.TcpListener を使用する場合、別のスレッドで着信接続 (.AcceptSocket) を処理する最良の方法は何ですか?
アイデアは、新しい着信接続が受け入れられたときに新しいスレッドを開始することですが、tcplistener はその後の着信接続に対して引き続き使用できます (そして、新しい着信接続ごとに新しいスレッドが作成されます)。接続を開始したクライアントとのすべての通信と終了は、スレッドで処理されます。
VB.NET コードの C# の例を歓迎します。
私が使用してきたコードは次のようになります。
class Server
{
private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false);
public void Start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 5555);
listener.Start();
while(true)
{
IAsyncResult result = listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
connectionWaitHandle.WaitOne(); // Wait until a client has begun handling an event
connectionWaitHandle.Reset(); // Reset wait handle or the loop goes as fast as it can (after first request)
}
}
private void HandleAsyncConnection(IAsyncResult result)
{
TcpListener listener = (TcpListener)result.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(result);
connectionWaitHandle.Set(); //Inform the main thread this connection is now handled
//... Use your TcpClient here
client.Close();
}
}
.NET の他の非同期操作と同じ方法で行うと思います。メソッドの BeginXxx バージョン、この場合は BeginAcceptSocket を呼び出します。コールバックはスレッド プールで実行されます。
プールされたスレッドは、通常、接続ごとのスレッドよりもはるかに優れたスケーリングを行います。数十の接続を超えると、システムは、実際の作業を完了するよりも、スレッド間の切り替えにはるかに多くの負荷をかけます。さらに、各スレッドには、通常 1MB のサイズの独自のスタックがあり (リンク フラグによって異なります)、2GB の仮想アドレス空間 (32 ビット システムの場合) で見つける必要があります。実際には、これにより 1000 未満のスレッドに制限されます。
.NET のスレッドプールが現在それを使用しているかどうかはわかりませんが、Windows にはスケーラブルな I/O を支援する I/O Completion Port と呼ばれるカーネル オブジェクトがあります。スレッドをこのオブジェクトに関連付けることができ、I/O 要求 (着信接続の受け入れを含む) を関連付けることができます。I/O が完了すると (接続が到着するなど)、Windows は待機中のスレッドを解放しますが、現在実行可能な (他の理由でブロックされていない) スレッドの数が、完了ポートに構成されているスケーラビリティ制限よりも少ない場合に限ります。通常、これはコア数の小さな倍数に設定します。
別のアプローチを提案したいと思います。私の提案では、2 つのスレッドのみを使用します。* 1 つのスレッドが着信接続をチェックします。* 新しい接続が開かれると、この情報は、現在開いているすべての接続を保持する共有データ構造に書き込まれます。* 2 番目のスレッドは、そのデータ構造を列挙し、開いている接続ごとに、送信されたデータを受信して応答を送信します。
このソリューションは、スレッド単位でよりスケーラブルであり、現在実装されている場合、開いている接続ごとに新しいスレッドを開くよりもパフォーマンスが向上するはずです。
O'Reilly C# 3.0 Cookbook に素晴らしい例があります。付属のソースは次の場所からダウンロードできます。http://examples.oreilly.com/9780596516109/CSharp3_0CookbookCodeRTM.zip
スレッドプールを使用すると、毎回新しいスレッドを開始する必要がなくなります (これはコストがかかるため)。クライアントは接続を閉じない可能性があるため、さらに接続を無期限に待つこともありません。クライアントを毎回同じスレッドにルーティングする方法を計画していますか?
すみません、サンプルがありません。