2013-02-22 43 views
2

我用下面的代碼來創建一個assyncronous TCP服務器:Assync TCP服務器的代碼示例分析

private void SetupServerSocket() 
{ 
    var myEndpoint = new IPEndPoint(IPAddress.Any, _port); 

    _serverSocket = new Socket(myEndpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
    _serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 
    _serverSocket.Bind(myEndpoint); 
    _serverSocket.Listen((int)SocketOptionName.MaxConnections); 
} 

protected void Open() 
{ 
    SetupServerSocket(); 
    Opened = true; 
    _serverSocket.BeginAccept(AcceptCallback, _serverSocket); 
} 

private void AcceptCallback(IAsyncResult result) 
{ 
    var connection = new ConnectionInfo(); 
    try 
    { 
     // Finish Accept 
     var s = (Socket)result.AsyncState; 
     connection.Socket = s.EndAccept(result); 
     connection.Buffer = new byte[8192]; 

     lock (_connections) 
     { 
      _connections.Add(connection); 
     } 

     // Start Receive and a new Accept 
     connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ReceiveCallback, connection); 
     _serverSocket.BeginAccept(AcceptCallback, result.AsyncState); 
    } 
    catch (SocketException ex) 
    { 
     CloseConnection(connection); 
    } 
    catch (Exception ex) 
    { 
     CloseConnection(connection); 
    } 
} 

private void CloseConnection(ConnectionInfo ci) 
    { 
     if (ci.Socket != null) 
     { 
      if (OnDisconnect != null) 
       OnDisconnect.Invoke((IPEndPoint)ci.Socket.RemoteEndPoint); 

      ci.Socket.Shutdown(SocketShutdown.Both); 
      ci.Socket.Close(); 
     } 

     lock (_connections) 
     { 
      _connections.Remove(ci); 
     } 
    } 

有一些事情是很難理解:

1 - 使用_serverSocket.SetSocketOption我啓用在TCP中使用KeepAlive。我發現默認情況下Windows的默認KeepAlive Timer爲2小時!並使用Wireshark確認此行爲。

經過一些Google搜索後,我在http://support.microsoft.com/kb/120642/EN-US中發現,您可以通過在註冊表中創建一個密鑰來更改Windows KeepAliveTime。我確實喜歡這個支持頁面指示,但KeepAlive Timer沒有應用(即使在重新啓動後),它仍然是2個小時。有誰知道如何更改Windows中的KeepAlive Timer?

2 - 代碼connection.Socket = s.EndAccept(result)可以拋出異常(通常是SocketException)。爲什麼Socket.EndResult會拋出SocketException

3 - 爲什麼我們必須將_serverSocket再次設置爲接受狀態,如果AcceptCallback()已經處於該狀態?

謝謝

回答

1
  1. 我曾與.NET類似問題保持連接定時器。我從來沒有找到一種方法來改變它,所以我們使用的解決方案是有一個後臺工作線程,如果還沒有發送其他真實數據,它會在給定時間段後發送少量數據(通常爲byte[1])。

  2. Socket.EndAccept()可以根據MSDN拋出預期的異常。如果其他地方的套接字發生了某些事情,例如套接字被關閉等,則通常會發生這些情況。您需要正確捕獲這些異常並決定如何讓程序繼續。在你的情況下,它看起來像你只是關閉連接。

  3. 調用_serverSocket.BeginAccept(AcceptCallback, result.AsyncState);將只觸發一次回撥,只要首先接受任何連接。之後,您必須決定是否要再接受未來的連接,如果是的話,請在回調中再次調用BeginAccept。

+0

在MSDN文檔中說,BeginAccept只會觸發一次回調? – RHaguiuda 2013-02-22 11:44:08

+0

「對於每個異步請求,此方法被調用一次」 - http://msdn.microsoft.com/en-us/library/system.asynccallback.aspx – MarcF 2013-02-22 11:49:10

+0

我可以在_serverSocket.BeginAccept(AcceptCallback,result.AsyncState)中設置AcceptCallback方法的第一行來保證服務器套接字將返回到Accept狀態? – RHaguiuda 2013-02-22 11:51:46