Async TCP Server 代码示例分析

Posted

技术标签:

【中文标题】Async TCP Server 代码示例分析【英文标题】:Assync TCP Server code example analysis 【发布时间】:2013-02-22 11:19:06 【问题描述】:

我正在使用以下代码创建一个异步 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 确认了此行为。

经过一番谷歌搜索后,我在http://support.microsoft.com/kb/120642/EN-US 中发现您可以通过在注册表中创建一个键来更改 Windows KeepAliveTime。我确实喜欢这个支持页面的说明,但是没有应用 KeepAlive 计时器(即使在重新启动后),它仍然是 2 小时。有谁知道如何在 Windows 中更改 KeepAlive 计时器?

2 - 代码connection.Socket = s.EndAccept(result) 可以抛出异常(通常是SocketException)。为什么Socket.EndResult 会抛出SocketException

3 - 如果AcceptCallback() 已经处于接受状态,为什么我们必须再次将_serverSocket 设置为接受状态?

谢谢

【问题讨论】:

【参考方案1】:

    我在使用 .net Keep Alive 计时器时遇到了类似问题。我从来没有找到改变它的方法,所以我们使用的解决方案是有一个后台工作线程来发送少量数据,通常是byte[1],如果没有发送其他真实数据,则在给定时间段之后。

    Socket.EndAccept() 可以根据MSDN 抛出预期的异常。如果其他地方的套接字发生了某些事情,例如套接字被关闭等,这些通常会发生。您需要正确捕获这些异常并决定您希望程序如何继续。在您的情况下,您似乎只是在关闭连接。

    调用_serverSocket.BeginAccept(AcceptCallback, result.AsyncState); 将只触发一次回调,无论哪个连接首先被接受。在此之后,您必须决定是否要接受更多的未来连接,如果是,请在回调中再次调用 BeginAccept。

【讨论】:

MSDN 文档中哪里说 BeginAccept 只会触发一次回调? "这个方法对每个异步请求调用一次" - msdn.microsoft.com/en-us/library/system.asynccallback.aspx 我可以在AcceptCallback方法的第一行设置_serverSocket.BeginAccept(AcceptCallback, result.AsyncState)来保证服务器socket返回Accept状态吗? EndAccept 成功返回后,您可以立即调用 BeginAccept。如果你反其道而行之,你就会遇到问题。

以上是关于Async TCP Server 代码示例分析的主要内容,如果未能解决你的问题,请参考以下文章

Boost tcp_server async_write 错误:访问冲突写入位置

Python 3.5 async/await 与真实代码示例

使用 libevent 广播 TCP-Server

tcp客户端构建流程

莫阻塞async代码

一个真实的Async/Await示例