从 TCP 服务器踢出客户端

Kicking clients from TCP server

我试图向我的服务器添加一个功能来踢出特定的客户端。但是客户端线程中的 Read 函数正在阻塞循环,客户端的踢出命令将不会执行,直到客户端发送另一条消息。所以我将它添加到服务器中的客户端线程; (顺便说一句,我真的不明白为什么我应该把 Poll 放在那里但是没有它它会抛出一个错误。)

if (client.Socket.Poll(10, SelectMode.SelectRead))
{
    readMessageSize = client.Stream.Read(readMessage, 0, _packetSize); 
}
else
{
    if (client.Kicked)
    {
        Console.WriteLine("The client [" + client.IP + "] has been kicked.");
        break;
    }

    Thread.Sleep(10);
    continue;
}

这一次,它在客户端的构造函数中给出了一个错误(这个构造函数将 returns 来自 Accept() 的套接字作为参数)说:"The operation is not allowed on a non-blocking Socket."。所以,我暂时像这样设置 Blocking true;

public ClientSocket(Socket client)
{
    _socket = client;
    _socket.Blocking = true;

    try
    {
        Stream = new NetworkStream(_socket);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

    _socket.Blocking = false;
}

我还将侦听器套接字的阻塞设置为 false 并更改了它,因为当我尝试 Abort() 侦听器线程时它不会,因为它卡在 Accept()

while (Running)
{
    try
    {
        if (_socket.Poll(10, SelectMode.SelectRead))
        {
            var newClientSocket = _socket.Accept();
            AddClient(newClientSocket);
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
        StopListen();
        break;
    }

    Thread.Sleep(250);
}

我觉得我在实施服务器方面走错了路。我准备好听取任何建议。我也可以接受任何可能对我有帮助的开源项目或代码片段。

关于您 post 编辑的代码:

  1. 不要在阻塞和非阻塞之间来回切换套接字。如果 .NET 不想要 NetworkStream 对象的非阻塞套接字,稍后将您提供给 NetworkStream 的套接字设置为非阻塞只会导致问题。
  2. 您不需要使用 Socket.Poll() 方法来正确执行此操作,但请务必注意 "microseconds" 之间存在差异(在 Poll() 方法中使用) 和 "milliseconds"(在 .NET 中几乎所有其他地方都用于整数时间间隔)。也许您真的打算在 Poll() 方法中只等待 10 微秒,但从您的 post 中并不完全清楚您的意图,所以我想确保您了解其中的区别。

就正确的方法而言:

  1. 首先要做的是实现非阻塞服务器,但要正确地进行。使用异步 API 选项之一。由于您使用的是 NetworkStream,最明显的方法是使用 ReadAsync() 方法。
  2. 实现非阻塞服务器后,终止客户端连接就像在某处维护超时逻辑一样简单(您不会阻塞线程I/O,但这也意味着您不需要没有可用于超时的 I/O 线程……您必须将超时逻辑放在其他地方)。
  3. 理想情况下,您将 "terminate" 客户端正常关闭,即调用 Socket.Shutdown() 方法,等待客户端关闭它的末端(即您看到接收操作完成 0-字节长度)。但是,如果必须(即 Socket.Close()),您可能可以通过重置连接来摆脱困境。两者都会导致异步读取操作完成,从而允许您清理客户端信息。