87

假设我正在运行一个简单的服务器并accept()从客户端建立了一个连接。

判断客户端何时断开连接的最佳方法是什么?通常,客户端应该发送关闭命令,但如果它手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理这个?

4

9 回答 9

141

在 TCP 中,只有一种方法可以检测到有序断开连接,那就是在读取时获取零作为返回值read()/recv()/recvXXX()

也只有一种可靠的方法来检测断开的连接:通过写入它。在对断开的连接进行足够多的写入之后,TCP 将进行足够的重试和超时以知道它已断开,并最终导致write()/send()/sendXXX()返回 -1,其errno/WSAGetLastError()值为ECONNRESET,或在某些情况下为“连接超时”。请注意,后者与“连接超时”不同,后者可能发生在连接阶段。

您还应该设置合理的读取超时,并丢弃失败的连接。

这里的答案是关于ioctl()FIONREAD竞争的废话。所做的只是告诉您套接字接收缓冲区中当前有多少字节可以在没有阻塞的情况下读取。如果客户在五分钟内没有向您发送任何内容,这并不构成断开连接,但确实会导致FIONREAD为零。不一样的东西:甚至不接近。

于 2013-07-15T22:31:34.070 回答
15

要对此进行更多扩展:

如果您正在运行服务器,则需要使用 TCP_KEEPALIVE 来监视客户端连接,或者自己做类似的事情,或者了解您在连接上运行的数据/协议。

基本上,如果连接被终止(即未正确关闭),那么服务器在尝试向客户端写入内容之前不会注意到,这就是 keepalive 为您实现的。或者,如果您更了解协议,则无论如何都可以在不活动超时时断开连接。

于 2008-11-12T11:41:12.613 回答
2

如果您使用带有完成例程或完成端口的重叠(即异步)I/O,当客户端关闭连接时,您将立即收到通知(假设您有未完成的读取)。

于 2008-11-12T11:49:04.580 回答
0

尝试寻找 EPOLLHUP 或 EPOLLERR。 如何检查客户端连接是否仍然存在

在某些情况下,读取和查找 0 会起作用,但不是全部。

于 2015-12-21T19:22:48.637 回答
0

TCP 在协议中有“打开”和“关闭”程序。一旦“打开”,连接就会保持到“关闭”。但是有很多事情可以异常阻止数据流。话虽如此,确定是否可以使用链路的技术高度依赖于协议和应用程序之间的软件层。上面提到的那些专注于试图以非侵入方式(读取或写入 0 字节)使用套接字的程序员可能是最常见的。库中的某些层将为程序员提供“轮询”。例如,Win32 异步(延迟)调用可以启动读取,该读取将返回没有错误和 0 字节,以指示无法再读取的套接字(可能是 TCP FIN 过程)。其他环境可能会使用“事件” 如其包装层中所定义。这个问题没有单一的答案。检测何时无法使用套接字并应关闭套接字的机制取决于库中提供的包装器。还值得注意的是,套接字本身可以被应用程序库下面的层重用,因此弄清楚您的环境如何处理 Berkley 套接字接口是明智的。

于 2016-07-19T04:47:46.467 回答
-3

这真的很容易做到:可靠而不混乱:

        Try
            Clients.Client.Send(BufferByte)
        Catch verror As Exception
            BufferString = verror.ToString
        End Try
        If BufferString <> "" Then
            EventLog.Text &= "User disconnected: " + vbNewLine
            Clients.Close()
        End If
于 2009-10-06T13:43:37.140 回答
-3

我玩弄了一些解决方案,但这个似乎最适合检测 Windows 中的主机和/或客户端断开连接。它用于非阻塞套接字,源自IBM 的示例

char buf;
int length=recv(socket, &buf, 0, 0);
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0){
    return 0;
}   
if (nError==0){
    if (length==0) return 0;
}
于 2013-07-16T00:54:54.260 回答
-4

如果连接丢失,receive 的返回值将是 -1,否则它将是缓冲区的大小。

void ReceiveStream(void *threadid)
{
    while(true)
    {
        while(ch==0)
        {
            char buffer[1024];
            int newData;
            newData = recv(thisSocket, buffer, sizeof(buffer), 0);
            if(newData>=0)
            {
                std::cout << buffer << std::endl;
            }
            else
            {
                std::cout << "Client disconnected" << std::endl;
                if (thisSocket)
                {
                    #ifdef WIN32
                        closesocket(thisSocket);
                        WSACleanup();
                    #endif
                    #ifdef LINUX
                        close(thisSocket);
                    #endif
                }
                break;
            }
        }
        ch = 1;
        StartSocket();
    }
}
于 2017-10-05T09:12:35.623 回答
-6

select(设置了读取掩码)将返回带有信号的句柄,但是当您使用 ioctl* 检查待读取的字节数时,它将为零。这是套接字已断开连接的标志。

这是关于检查客户端是否已断开连接的各种方法的精彩讨论:Stephen Cleary,检测半开(丢弃)连接

* 对于 Windows 使用 ioctlsocket。

于 2008-11-12T09:07:00.897 回答