2013-02-22 54 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