2012-01-16 84 views
1

我正在用C#编写一个TCP服务器,并且已经遇到了一个奇怪的和潜在的安全问题。Telnet阻止C#TCP服务器

我的用于接受新连接基本拱形如下:

  1. A C#插座端口上监听,使用AcceptAsync方法以接受传入的连接。
  2. 使用ThreadPool旋转接受的连接来完成接受。

一切正常,但是如果有人telnet进入端口,一切都会停止。

症状:

  • 如果我远程登录到我的服务器和不发送任何数据(即不打任何键),服务器永远不会完成接受连接。

  • 我的SocketAsyncEventArgs.Completed回拨永远不会被telnet连接命中。

  • 更糟的是,所有进一步的连接都被阻止/排队,并且从未被我的代码接受。他们被放进一个CLOSE_WAIT状态:

    TCP 127.0.0.1:8221 chance:53960 CLOSE_WAIT

    TCP 127.0.0.1:8221 chance:53962 CLOSE_WAIT

    TCP 127.0.0.1:8221 chance:53964 CLOSE_WAIT

任何意见,将不胜感激。

StartAccept:

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事件。

+3

您是否在CLOSE_WAIT上搜索过?如果你有,你知道你为什么得到它。代码在哪里? – jgauffin 2012-01-16 22:36:25

+0

您可以指定连接尝试的超时时间吗? – Fantius 2012-01-17 00:02:51

+0

是的,我知道我的连接停留在一个错误的TCP状态。我正在寻找解决方案。目前,恶意用户可能通过简单地使用telnet连接到服务器来挂起我的服务器。 – Stryck 2012-01-17 00:10:14

回答

1

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

的错误是这行:

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

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

MSDN

所需的最小缓冲器大小是288个字节。如果指定了较大的缓冲区大小,则Socket将期望除Winsock AcceptEx调用接收到的地址数据以外的其他额外数据,并将等待直到收到额外的数据。

我纠正代码:

// 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个字节或更小的缓冲似乎并没有解决问题。只有将缓冲区设置为空时,连接时才会引发Completed事件,而不发送数据。

0

看起来GetConnection在某处被阻塞。 通常,如果服务器不在重负载下,异步操作可能以同步方式完成。 另外AcceptAsync返回false或回调方法被调用的事实意味着异步操作已完成,您的代码应分析结果。

下面是一个简单的异步TCP服务器框架,它异步接受连接。

void StartServer() 
{ 
    Socket serverSocket = new Socket(addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
    serverSocket.Bind(new IPEndPoint(addr, port)); 
    s.Listen(5000); 

    SocketAsyncEventArgs args = new SocketAsyncEventArgs(); 
    args.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptCompleted); 
    args.UserToken = serverSocket; 

    if (!serverSocket.AcceptAsync(args)) 
     AcceptCompleted(this, args); 
} 

void AcceptCompleted(object obj, SocketAsyncEventArgs args) 
{ 
    Socket client = args.AcceptSocket; 
    if (args.SocketError != SocketError.Success) 
     return; 

    StartClientOperations(args.AcceptSocket); 

    args.AcceptSocket = null; 
    Socket s = (Socket)args.UserToken; 
    if (!s.AcceptAsync(args)) 
     AcceptCompleted(this, args); 
} 

void StartClientOperations(Socket newClient) 
{ 
    //start other asynchronous operations here with the client socket 
} 
+0

GetConnection()发生在建立TCP连接之前。它也只是一个简单的连接池队列的链接。 – Stryck 2012-01-17 17:41:34

+0

在你的代码中,GetConnection在实际的TCP连接被接受后被调用。你能详细阐述一下简单连接池队列以及它在你的应用程序中的作用吗? – 2012-01-17 18:33:52