c# - Async TCP Server 代码示例分析

标签 c# .net sockets tcp asyncsocket

我正在使用以下代码创建异步 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 默认有 2 小时的默认 KeepAlive 定时器!并且使用 Wireshark 确认了此行为。

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

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

3 - 为什么我们必须在 AcceptCallback() 中再次将 _serverSocket 设置为 Accept 状态?

谢谢

最佳答案

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

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

  3. 调用 _serverSocket.BeginAccept(AcceptCallback, result.AsyncState); 将仅针对首先接受的连接触发一次回调。在此之后,您必须决定是否要接受进一步的 future 连接,如果是,请在回调中再次调用 BeginAccept。

关于c# - Async TCP Server 代码示例分析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15022985/

相关文章:

c# - c#中发生catch错误时如何跳过下一行的执行?

C# BinaryWriter 长度前缀 - UTF7 编码

c# - 我可以复制一个项目的一些引用并将其粘贴到 Visual Studio 中另一个项目的引用吗?

c# - 为什么我不能在范围内的 switch 语句中使用 "constant"?

.net - 为 .NET Core/UWP 中的单个 HTTP 请求设置自定义 WebProxy

c# - 如何在wpf中实现带有清除按钮的文本框?

c# - 从数组中删除一项并将其他项向后移动

java - 当我尝试发送请求时出现未知主机异常

sockets - 我怎么知道服务名称?

c++ - boost asio 中具有异步功能的坏字符