4

私は、ソケットクラスの非同期メソッド(BeginAccept()、BeginReceive()など)を使用するかなり汎用的なC#ソケットサーバーを持っています。このサーバーは、WinServer2003を実行している多くの顧客サイトで過去4年間うまく機能しています。 Windows Server 2008 R2サーバー、64ビットにインストールしました。最初のクライアントが接続し、acceptハンドラーでBeginReceive()およびBeginAccept()呼び出しを発行するまで、すべてが正常に見えます。これが発生すると、CPU使用率が100%に急上昇し、リスニングソケットを閉じるまでその状態が続きます。

重要かどうかはわかりませんが、サーバーは仮想マシンで実行されています。

多くのテストを行いましたが、何も役に立たないようです。Process Explorerを使用すると、BeginReceive()/ BeginAccept()呼び出しの直後に2つのスレッドがスピンアップし、それらがプロセッサーを消費していることがわかります。残念ながら、Win764ビットワークステーションではこの問題を再現できません。

私は多くの調査を行いましたが、これまでに見つけたのは、Server2008R2でTCP/IPコンポーネントに問題がある可能性があることを示唆する、次の2つのKB記事ですが、これらはホットフィックスとしてのみ入手可能です:KB2465772およびKB2477730。問題が解決することが確実になるまで、顧客にインストールしてもらうのは気が進まない。

他の誰かがこの問題を抱えていましたか?もしそうなら、あなたはこの問題を解決するために何をしなければなりませんでしたか?

これが状況を引き起こすと私が信じる方法です:

private void AcceptCallback(IAsyncResult result) {
 ConnectionInfo connection = new ConnectionInfo();

 try {
    // Finish accept.
    Socket listener = (Socket)result.AsyncState;
    connection.Socket = listener.EndAccept(result);
    connection.Request = new StringBuilder(256);

    // Start receive and a new accept.
    connection.Socket.BeginReceive(connection.Buffer, 0,
       connection.Buffer.Length, SocketFlags.None,
       new AsyncCallback(ReceiveCallback), connection);

    _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), listener);

    // CPU usage spikes at 100% shortly after this...

 }
 catch (ObjectDisposedException /*ode*/) {
    _log.Debug("[AcceptCallback] ObjectDisposedException");
 }
 catch (SocketException se) {
    connection.Socket.Close();
    _log.ErrorFormat("[AcceptCallback] Socket Exception ({0}: {1} {2}", connection.ClientAddress, se.ErrorCode, se.Message);
 }
 catch (Exception ex) {
    connection.Socket.Close();
    _log.ErrorFormat("[AcceptCallback] Exception {0}: {1}", connection.ClientAddress, ex.Message);
 }
}
4

2 に答える 2

2

この問題は、リスナーソケットを設定するときにBeginAccept()を複数回呼び出すことで発生しました。問題が64ビットサーバーでのみ発生する理由はわかりませんが、以下に示すようにコードを変更すると問題が修正されました。

元のコード:

public SetupServerSocket() {
   IPEndPoint myEndPoint = new IPEndPoint(IPAddress.Any, _port);

   // Create the socket, bind it, and start listening.
  _serverSocket = new Socket(myEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  _serverSocket.Bind(myEndPoint);
  _serverSocket.Listen((int)SocketOptionName.MaxConnections);

  for (int i = 0; i < 10; i++) {
     _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket);
  }
}

次のように:

public SetupServerSocket() {
   IPEndPoint myEndPoint = new IPEndPoint(IPAddress.Any, _port);

   // Create the socket, bind it, and start listening.
  _serverSocket = new Socket(myEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  _serverSocket.Bind(myEndPoint);
  _serverSocket.Listen((int)SocketOptionName.MaxConnections);

  //for (int i = 0; i < 10; i++) {
     _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket);
  //}
}
于 2011-07-14T04:04:33.167 に答える
1

SetupServerSocket()を見つけた「WirePerformance」の記事を知っています。元の10x forループは、新しいクライアント接続が高速な場合に10個のリッスンスレッドをサポートすることです。1つのリスナーに変更しました。Win2k8r2にそのようなバグがある場合、おそらくそれが唯一の可能な解決策です。クライアントに堅牢な接続再試行コードがあることを確認することをお勧めします。

.NEThttp: //msdn.microsoft.com/en-us/magazine/cc300760.aspxの高性能ソケットを使用してワイヤに近づきます

于 2012-07-24T22:27:39.973 に答える