2016-04-07 19 views
1

私は高性能サーバーを作成する方法についてソケットプログラミングと読書を見てきました。接続を受け入れた後にリスニング状態に戻すソケットを置くためにSocketAcceptAsync方法を使用して、どのように時々呼び出しがsyncronously実行(およびfalseを返す)も接続がすぐに利用可能である場合に、どのように読んソケットリスナー複数同時AcceptAsync呼び出し

、私がし始めましたたとえ1000クライアントが一度に接続されていても、クライアントがどれくらい速くパススルーできるかによっては、最終的には1つの新しい接続を一度に処理するだけで、AcceptAsyncを使用する接続を受け入れるソケットは非同期的に「待機」します処理のための別のスレッドに、パフォーマンスを制限することがあります。

これは私が意図していたオブジェクトプールではなく、すべての接続にSocketAsyncEventArgsという単一のインスタンスを使用して、このことを念頭に置いてクラスを作成できるかどうか疑問に思いました。

このことについてもっと考えてみると、接続が利用できない間に複数のAcceptAsyncコールが行われた場合に何が起こるのかと疑問に思った。だから私は何が起こるか見るために、この非常に非常にラフなテストアプリケーションを作っ:

class Program 
{ 
    private static Socket s; 

    static void Main(string[] args) 
    { 
     s = new Socket(IPAddress.Any.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
     s.Bind(new IPEndPoint(IPAddress.Any, 10001)); 
     s.Listen(10); 
     var e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
     while (true) 
     { 

     } 
    } 

    private static void EOnCompleted(object sender, SocketAsyncEventArgs socketAsyncEventArgs) 
    { 
     Console.WriteLine(socketAsyncEventArgs.AcceptSocket.RemoteEndPoint); 
     Thread.Sleep(10000); 
     var e = new SocketAsyncEventArgs(); 
     e.Completed += EOnCompleted; 
     s.AcceptAsync(e); 
    } 
} 

私が接続してアプリケーションをスパムへのTCPテストアプリケーションを使用して、結果が複数のAcceptAsync呼び出しは、複数の/受諾の完全なサイクルを作成したことを示しました。並行して実行されたようだ。

私の質問は、実際には何か利点がありますか?着信接続の処理を高速化するには正当な方法ですか?何か欠点はありますか?

EDIT 1:データを常に非同期呼び出し用に処理することができれば、それらはすべて同期的に実行され、新しい接続を受け入れることはできません。かなりの遅れがある。

高性能サーバーを実際に作成するにはどうすればよいですか。

答えて

1

通常、必要とされるすべては、このコードです:

while (true) { 
var socket = listener.Accept(); 
Task.Run(async() => await RunConnectionAsync(socket)); 
} 

このコードのユーザーモードの部分は非常に迅速に実行されます。ほぼすべての時間がカーネルに費やされます。これにより、接続が非常に迅速に受け入れられます。カーネル管理されたバックログのため、接続は切断されません。

あなたのperf目標に達するために複数の未処理の受け入れ呼び出しを行う必要はほとんどありません。必要な場合は、複数のスレッドで同じコードを実行するだけです。

SocketAsyncEventArgs APIは廃止になっています。タスクベースのIOははるかに便利で、CPUを多少使用するだけです。

特に、操作ごとに新しいSocketAsyncEventArgsを作成すると、そのパターンのすべての利点が失われます。それは無意味です。

ほとんどの場合、ソケットから受け入れるために非同期IOは必要ありません。実行にはより多くのCPU時間がかかり、したがって待ち時間はわずかに長くなります。

+0

ありがとう、これは非常に有用です、インターネットは、答えを氾濫させ、それを行うためのあらゆる方法が最良の方法である方法についての10ページの記事を流しています。私の唯一の関心事は、長期実行タスクに 'Task'を使用するのが適切かどうかです。私のサーバーには何千もの接続が長時間オープンしています。私が各ソケットへの完全な同期アクセスを使用した場合、それは私が意味することですが、何千ものタスクが常に実行されています。それは受け入れられますか? – Ashigore

+0

これは、awaitを使用した非同期ソケットIOの素晴らしいケースです。あなたは1000sの接続をそうすることができます(スレッドを消費するわけではありません)。しかし、acceptループはちょうど1スレッドです。誰も気にしない。通常は同期IOである、あなたにとって便利なことをしてください。 – usr

+0

したがって、async IOと待機を使用してRunConnectionAsyncを実装します。 acceptループは、この答えに立っているので正しいです。 – usr

関連する問題