从 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 编辑的代码:
- 不要在阻塞和非阻塞之间来回切换套接字。如果 .NET 不想要
NetworkStream
对象的非阻塞套接字,稍后将您提供给 NetworkStream
的套接字设置为非阻塞只会导致问题。
- 您不需要使用
Socket.Poll()
方法来正确执行此操作,但请务必注意 "microseconds" 之间存在差异(在 Poll()
方法中使用) 和 "milliseconds"(在 .NET 中几乎所有其他地方都用于整数时间间隔)。也许您真的打算在 Poll()
方法中只等待 10 微秒,但从您的 post 中并不完全清楚您的意图,所以我想确保您了解其中的区别。
就正确的方法而言:
- 首先要做的是实现非阻塞服务器,但要正确地进行。使用异步 API 选项之一。由于您使用的是
NetworkStream
,最明显的方法是使用 ReadAsync()
方法。
- 实现非阻塞服务器后,终止客户端连接就像在某处维护超时逻辑一样简单(您不会阻塞线程I/O,但这也意味着您不需要没有可用于超时的 I/O 线程……您必须将超时逻辑放在其他地方)。
- 理想情况下,您将 "terminate" 客户端正常关闭,即调用
Socket.Shutdown()
方法,等待客户端关闭它的末端(即您看到接收操作完成 0-字节长度)。但是,如果必须(即 Socket.Close()
),您可能可以通过重置连接来摆脱困境。两者都会导致异步读取操作完成,从而允许您清理客户端信息。
我试图向我的服务器添加一个功能来踢出特定的客户端。但是客户端线程中的 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 编辑的代码:
- 不要在阻塞和非阻塞之间来回切换套接字。如果 .NET 不想要
NetworkStream
对象的非阻塞套接字,稍后将您提供给NetworkStream
的套接字设置为非阻塞只会导致问题。 - 您不需要使用
Socket.Poll()
方法来正确执行此操作,但请务必注意 "microseconds" 之间存在差异(在Poll()
方法中使用) 和 "milliseconds"(在 .NET 中几乎所有其他地方都用于整数时间间隔)。也许您真的打算在Poll()
方法中只等待 10 微秒,但从您的 post 中并不完全清楚您的意图,所以我想确保您了解其中的区别。
就正确的方法而言:
- 首先要做的是实现非阻塞服务器,但要正确地进行。使用异步 API 选项之一。由于您使用的是
NetworkStream
,最明显的方法是使用ReadAsync()
方法。 - 实现非阻塞服务器后,终止客户端连接就像在某处维护超时逻辑一样简单(您不会阻塞线程I/O,但这也意味着您不需要没有可用于超时的 I/O 线程……您必须将超时逻辑放在其他地方)。
- 理想情况下,您将 "terminate" 客户端正常关闭,即调用
Socket.Shutdown()
方法,等待客户端关闭它的末端(即您看到接收操作完成 0-字节长度)。但是,如果必须(即Socket.Close()
),您可能可以通过重置连接来摆脱困境。两者都会导致异步读取操作完成,从而允许您清理客户端信息。