在 .NET 中检测客户端断开连接的最佳实践?

发布于 2024-08-03 10:23:47 字数 771 浏览 12 评论 0原文

我正在用 C# 开发一台服务器,它只能接受一个客户端,我需要知道该客户端何时断开连接才能接受另一个连接请求。

我正在使用第一个 Socket,它通过 Socket.BeginAccept 持续侦听连接请求并接受或拒绝客户端。当客户端接受时,Socket.EndAccept 返回的新 Socket 用于客户端和服务器之间的通信。然后,服务器使用 Socket.Begin/EndReceive 等待来自客户端的命令并发送响应。服务器使用类似 Telnet 的协议,这意味着每个命令和每一行响应都必须以 \r\n 结尾。

为了检测客户端是否已断开连接,我安装了一个计时器,该计时器每 500 毫秒向客户端发送一条空消息(“\r\n”)。如果客户端断开连接,Socket 会抛出异常。服务器捕获此异常,关闭当前会话并接受新连接。该解决方案很强大,但意味着网络上不需要的流量,并且必须由客户端正确处理,客户端必须在获得实际响应之前过滤虚拟消息。

我尝试发送一个空缓冲区(Socket.Send(new byte[1], 0, 0)),但似乎在服务器->客户端方向上不起作用。

另一种解决方案可能是处理 Socket.EndReceive 返回 0 字节的情况。对于在“空闲”时间内发生的断开连接的情况,它工作得很好。但是,如果客户端在消息传输期间断开连接,服务器并不总是能看到它并无限期地等待。

我已经看到了有关此问题的多个线程和问题,但我从未见过任何好的解决方案。

所以我的问题是:检测 .Net 中断开连接的最佳方法是什么?

I'm developing a server in C# which can accept only one client and I need to know when this client is disconnected to be able to accept another connection requests.

I'm using a first Socket which continuously listen to connection request with Socket.BeginAccept and accept or reject clients. When a client is accepted, the new Socket which is returned by Socket.EndAccept is used for the communication between client and server. Then, the server waits for commands from the client with Socket.Begin/EndReceive and sends responses. The server uses a Telnet-like protocol, which means that each command and each line of response must end with \r\n.

To detect if the client has been disconnected, I've installed a timer which sends every 500ms an empty message ("\r\n") to the client. If the client is disconnected, an exception is thrown by the Socket. This exception is caught by the server which closes the current session and accepts new connection. This solution is robust but implies unneeded traffic over the network and must be handled correctly by the client which must filter dummy messages before getting an actual response.

I've tried to send an empty buffer (Socket.Send(new byte[1], 0, 0)), but it seems that doesn't work in the direction server->client.

Another solution might be to handle the case where Socket.EndReceive returns 0 byte. It works fine for the case of a disconnection which occurs during "idle" time. But if the client disconnects during a message transfert, the server doesn't always see it and waits indefinitely.

I've already seen several threads and questions about this problem, but I've never seen any good solution.

So my question is: what is the best way to detect a disconnection in .Net ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

枫以 2024-08-10 10:23:47

唯一的其他选择是,如果是 TCP,则让 TCP 每隔一段时间发送一个保持活动状态,这仍然是轮询,就像您现在正在做的那样,但在 TCP 层处理,因此您的协议不需要知道。

然而,没有办法绕过轮询,因为如果不向其他客户端发送某些内容并获得响应,您就无法知道它是否仍然处于连接状态。

当通过状态数据包检查(例如标准 NAPT)进行通信时,无论如何也可能需要保持活动状态,以避免远程服务器由于不活动而丢弃会话。

The only other option is if it is TCP is to have TCP send a keep-alive every so often, which is still polling such as what you're doing now but handled at the TCP layer so you're protocol doesn't need to know.

There's no way around the polling however since without sending something to the other client and getting a response you have no way to know whether it's still connected or not.

Keep alive may also be required anyway when communicating through stateful packet inspection such as standard NAPT to avoid having the remote server drop the session due to in-activity.

野の 2024-08-10 10:23:47

您可以使用以下方法来查找客户端是否仍然连接。这个

public static bool IsConnected(this TcpClient client)
{
    try
    {
        bool connected = !(client.Client.Poll(1, SelectMode.SelectRead) && client.Client.Available == 0);

        return connected;
    }
    catch
    {
        return false;
    }
}

这个answer 用于测试套接字,这就是我获取代码片段的方式。

You can use the below method to find if a client is still connected. This

public static bool IsConnected(this TcpClient client)
{
    try
    {
        bool connected = !(client.Client.Poll(1, SelectMode.SelectRead) && client.Client.Available == 0);

        return connected;
    }
    catch
    {
        return false;
    }
}

This answer is for testing socket, which is how I got my code snippet.

暮光沉寂 2024-08-10 10:23:47

你对客户有控制权吗?如果是这样,你不能让客户端发送一个特殊的数据包到服务器说它正在断开连接,然后断开套接字吗?

您是否试图检测客户端实际上已断开连接的情况(使用 Socket.Shutdown() 或 Socket.Close()),或者客户端是否闲置了很长一段时间,因此需要被弹出?

无论哪种情况,都让客户端向服务器发送定期消息(就像您所做的那样)。服务器可以跟踪最后一次心跳,如果客户端错过超过3次心跳,则可以断开客户端连接。是的,它涉及额外的数据,但从总体上看这还不算太糟糕。如果您对其进行调整,使其以足够好的周期发送心跳,这样您就可以了解客户端是否还活着,并且心跳之间的间隔时间不会太长,那么您可以拥有一个非常好的系统。

Do you have control over the client? If so, cant you just have the client send a special packet to the server saying that it is disconnecting, and then disconnect the socket?

Are you trying to detect the case that the client has actually disconnected (using either Socket.Shutdown(), or Socket.Close()) or whether the client is idle for a huge amount of time, and therefore needs to be ejected?

In either case, have the client send a periodic message (just as you have done) to the server. The server can keep track of the last heartbeat, and if the client misses more than 3 heartbeats, you can disconnect the client. yes, it involves extra data, but that is not too bad in the grand scheme of things. If you tune it so that it sends the heartbeat at a good enough period, so that you get the advantage of knowing if the client is alive, and do not go too long between heartbeats, you could have a very good system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文