c# - Telnet 阻塞 C# TCP 服务器

标签 c# tcp telnet asyncsocket

我正在用 C# 编写一个 TCP 服务器,遇到了一个奇怪的问题,而且可能是一个安全问题。

我接受新连接的基本架构如下:

  1. 监听端口的 C# 套接字,使用 AcceptAsync 方法接受传入连接。
  2. 使用 ThreadPool 分离已接受的连接以完成接受。

一切都运行良好,但是如果有人 telnet 到端口,一切都会停止。

症状:

  • 如果我 telnet 到我的服务器并且不发送任何数据(即不按任何键),服务器将永远不会完成接受连接。

  • 我的 SocketAsyncEventArgs.Completed 回调从未针对 telnet 连接命中。

  • 更糟糕的是,所有进一步的连接都被阻止/排队并且永远不会被我的代码接受。它们被置于 CLOSE_WAIT 状态:

    TCP 127.0.0.1:8221 机会:53960 CLOSE_WAIT

    TCP 127.0.0.1:8221 chance:53962 CLOSE_WAIT

    TCP 127.0.0.1:8221 chance:53964 CLOSE_WAIT

如有任何建议,我们将不胜感激。

开始接受:

private void StartAccept(SocketAsyncEventArgs AcceptArgs)
{
    CurrentAcceptArgs = AcceptArgs;
    AcceptArgs.AcceptSocket = null;

    if (AcceptArgs.Buffer == null ||
        AcceptArgs.Buffer.Length < 1024)
    {
        AcceptArgs.SetBuffer(new byte[1024], 0, 1024);
    }

    if (MainSocket != null)
    {
        lock (MainSocket)
        {
            // If this is false, we have an accept waiting right now, otherwise it will complete aynsc
            if (MainSocket.AcceptAsync(AcceptArgs) == false)
            {
                ThreadPool.QueueUserWorkItem(FinishAccept, AcceptArgs);
                StartAccept(GetConnection());
            }
        }
    }
}

接受连接的完成回调:

protected override void OnIOCompleted(object sender, SocketAsyncEventArgs e)
{
    PWClientRemote RemoteClient = e.UserToken as PWClientRemote;

    // Determine which type of operation just completed and call the associated handler.
    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Accept:
            StartAccept(GetConnection());
            ThreadPool.QueueUserWorkItem(FinishAccept, e);
            break;
        default:
            base.OnIOCompleted(sender, e);
            break;
    }
}

完成接受:

private void FinishAccept(object StateObject)
{
    SocketAsyncEventArgs args = (SocketAsyncEventArgs)StateObject;
    FinishAcceptInternal(args);
}

这是连接 telnet 但在发送数据之前的 wireshark:

No.     Time        Source                Destination           Protocol Length Info
  1 0.000000    192.168.1.146         192.168.1.109         TCP      66     59766 > 8221 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
  2 0.000076    192.168.1.109         192.168.1.146         TCP      66     8221 > 59766 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
  3 0.000389    192.168.1.146         192.168.1.109         TCP      60     59766 > 8221 [ACK] Seq=1 Ack=1 Win=65536 Len=0

这应该是建立连接的完整握手,但从未引发 Completed 事件。

最佳答案

回答我自己的问题,因为我找到了根本原因:

错误是这一行:

if (AcceptArgs.Buffer == null ||
    AcceptArgs.Buffer.Length < 1024)
{
    AcceptArgs.SetBuffer(new byte[1024], 0, 1024);
}

这是因为如果您设置缓冲区,AcceptAsync 将阻塞直到它接收到一些数据。

来自 MSDN :

The minimum buffer size required is 288 bytes. If a larger buffer size is specified, then the Socket will expect some extra data other than the address data received by the Winsock AcceptEx call and will wait until this extra data is received.

我更正的代码:

// We set a null buffer here.
// If we set a valid buffer, the accept will expect data
// and will hang unless it gets it.
AcceptArgs.SetBuffer(null, 0, 0);

我不确定这是否是正确的修复,设置 288 字节或更小的缓冲区似乎无法解决问题。仅将缓冲区设置为 null 会导致在连接但不发送数据时引发 Completed 事件。

关于c# - Telnet 阻塞 C# TCP 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8887190/

相关文章:

linux - 如何在 perl 脚本中的 telnet 命令提示符下运行命令

c# - 尝试运行 Windows 服务时出现 LyncClientException

c# - 有没有办法将通过 TaskCompletionSource 驱动的任务设置为 Status 'Running' ?

wcf - 在 WCF TCP 绑定(bind)的情况下如何启用 SSL 和证书

linux - 如何将输出命令 telnet 放入文件中

linux - 将 spawn 与 Expect 一起使用时僵尸 telnet 进程堆积

c# - 是否可以从 C# 读取内部 CPU 节拍计数器?

C# 使用异常进行流控制

C# TcpClient,同时读写流

linux - 在没有服务器调用 'connect' 的情况下,套接字上的 'accept' 调用能否成功返回?