检测TCP客户端断开连接

时间:2008-11-12 09:02:48

标签: c++ sockets tcp

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

告诉客户端何时断开连接的最佳方式是什么?通常情况下,客户端应该发送一个关闭命令,但如果它手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理此问题?

13 个答案:

答案 0 :(得分:96)

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

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

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

这里关于ioctl()FIONREAD的答案是无意义的。所有这一切都告诉您当前在套接字接收缓冲区中有多少字节,可以无阻塞地读取。如果客户端没有向您发送任何不构成断开连接的五分钟内的任何内容,但它确实会导致FIONREAD为零。不一样:甚至没有关闭。

答案 1 :(得分:12)

进一步扩展这个:

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

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

这是关于检查客户端已断开连接的各种方法的一个很好的讨论:Stephen Cleary, Detection of Half-Open (Dropped) Connections

* for Windows使用ioctlsocket。

答案 4 :(得分:0)

"""
tcp_disconnect.py
Echo network data test program in python. This easily translates to C & Java.

A server program might want to confirm that a tcp client is still connected 
before it sends a data. That is, detect if its connected without reading from socket.
This will demonstrate how to detect a TCP client disconnect without reading data.

The method to do this:
1) select on socket as poll (no wait)
2) if no recv data waiting, then client still connected
3) if recv data waiting, the read one char using PEEK flag 
4) if PEEK data len=0, then client has disconnected, otherwise its connected.
Note, the peek flag will read data without removing it from tcp queue.

To see it in action: 0) run this program on one computer 1) from another computer, 
connect via telnet port 12345, 2) type a line of data 3) wait to see it echo, 
4) type another line, 5) disconnect quickly, 6) watch the program will detect the 
disconnect and exit.

John Masinter, 17-Dec-2008
"""

import socket
import time
import select

HOST = ''       # all local interfaces
PORT = 12345    # port to listen

# listen for new TCP connections
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)
# accept new conneciton
conn, addr = s.accept()
print 'Connected by', addr
# loop reading/echoing, until client disconnects
try:
    conn.send("Send me data, and I will echo it back after a short delay.\n")
    while 1:
        data = conn.recv(1024)                          # recv all data queued
        if not data: break                              # client disconnected
        time.sleep(3)                                   # simulate time consuming work
        # below will detect if client disconnects during sleep
        r, w, e = select.select([conn], [], [], 0)      # more data waiting?
        print "select: r=%s w=%s e=%s" % (r,w,e)        # debug output to command line
        if r:                                           # yes, data avail to read.
            t = conn.recv(1024, socket.MSG_PEEK)        # read without remove from queue
            print "peek: len=%d, data=%s" % (len(t),t)  # debug output
            if len(t)==0:                               # length of data peeked 0?
                print "Client disconnected."            # client disconnected
                break                                   # quit program
        conn.send("-->"+data)                           # echo only if still connected
finally:
    conn.close()

答案 5 :(得分:0)

尝试寻找EPOLLHUP或EPOLLERR。 How do I check client connection is still alive

在某些情况下,阅读和寻找0会有效,但不是全部。

答案 6 :(得分:0)

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

答案 7 :(得分:-1)

如果连接丢失,接收的返回值将为-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();
    }
}

答案 8 :(得分:-1)

在python中,您可以执行如下try-except语句:

try:
  conn.send("{you can send anything to check connection}")
except BrokenPipeError:
  print("Client has Disconnected")

之所以起作用,是因为当客户端/服务器关闭程序时,python会根据断开连接的人将断开的pip错误返回给服务器或客户端。

答案 9 :(得分:-2)

来自apache项目的apr库是这个问题的一个很好的参考。它使用带超时值的poll来检查另一端连接是否损坏。

答案 10 :(得分:-2)

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

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;
}

答案 11 :(得分:-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

答案 12 :(得分:-4)

当检测到PC上的电缆移除问题时,我们遇到了类似的问题。谷歌搜索后,我们点击提供此功能的SuperCom for TCP库和一个非常可靠的数据通信库,该库也可以在连接关闭时处理报告事件。