检测TCP客户端断开
比方说,我正在运行一个简单的服务器,并从客户端accept()
一个连接。
什么是最好的方式来告诉客户端何时断开连接? 通常情况下,客户端应该发送一个closures命令,但是如果手动断开连接或者完全失去networking连接呢? 服务器如何检测或处理这个?
select(带有读取的掩码集)将返回带有处理信号的句柄,但是当您使用ioctl *检查待读取的字节数时,它将为零。 这是一个sockets已经断开的标志。
这是关于检查客户端已经断开连接的各种方法的一个很好的讨论: Stephen Cleary,半开放(丢弃)连接的检测 。
*用于Windows使用ioctlsocket。
在TCP中,只有一种方法可以检测有序的断开连接,并且read()/recv()/recvXXX()
从read()/recv()/recvXXX()
获取返回值为零。
只有一个可靠的方法来检测连接断开:通过写入。 在写足够多的连接之后,TCP会做足够的重试和超时,以便知道它已经损坏,最终会导致write()/send()/sendXXX()
返回-1,其中errno/WSAGetLastError()
值为ECONNRESET,
或在某些情况下“连接超时”。 请注意,后者与“连接超时”不同,后者可能发生在连接阶段。
您还应该设置一个合理的读取超时,并放弃连接失败。
这里关于ioctl()
和FIONREAD
是无稽之谈。 所做的只是告诉你在套接字接收缓冲区中有多less个字节,可以在没有阻塞的情况下读取。 如果客户在五分钟内没有给您发送任何不构成断开连接的任何信息,但确实会导致FIONREAD
为零。 不一样的事情:甚至不接近。
为了扩大这一点:
如果您正在运行服务器,则需要使用TCP_KEEPALIVE来监视客户端连接,或者自己做类似的事情,或者了解您通过连接运行的数据/协议。
基本上,如果连接被杀死(即没有正确closures),服务器将不会注意到,直到它试图写一些东西给客户端,这就是keepalive为你实现的。 或者,如果您更好地了解协议,则无论如何您都可以断开不活动超时连接。
如果使用完成例程或完成端口的重叠(即asynchronous)I / O,则当客户端closures连接时,将立即通知您(假设您有未完成的读取)。
""" 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=%sw=%se=%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()
尝试寻找EPOLLHUP或EPOLLERR。 我如何检查客户端连接仍然活着
阅读和寻找0将在某些情况下工作,但不是全部。
TCP在协议中有“开放”和“closures”的程序。 一旦“打开”,连接将保持到“closures”。 但是有很多东西可以阻止数据stream的exception。 也就是说,确定是否可以使用链接的技术高度依赖于协议和应用程序之间的软件层。 上面提到的那些重点是试图以非侵入方式使用套接字的程序员(读或写0字节)可能是最常见的。 库中的一些图层将为程序员提供“轮询”。 例如,Win32 asych(延迟)调用可以启动一个无错误返回的读取操作,0字节发送一个无法读取的套接字(大概是TCP FIN程序)。 其他环境可能使用在其包装层中定义的“事件”。 这个问题没有单一的答案。 检测套接字何时不能使用并应该closures的机制取决于库中提供的包装器。 值得一提的是,套接字本身可以被应用程序库下面的图层重复使用,所以明智地弄清楚你的环境如何处理Berkley套接字接口。
如果连接丢失,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(); } }
apache项目的apr库是这个问题的一个很好的参考。 它使用轮询超时值来检查对方连接是否损坏。
我玩了几个解决scheme,但这似乎最适合在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; }
我们遇到类似的问题时,检测到电脑上的电缆拆除是问题。 谷歌search后,我们击中了提供此function的SuperCom for TCP库和一个非常可靠的数据通信库,当连接closures时也可以处理报告事件。
这很容易做到:可靠而不混乱:
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
这个问题是build立在对networking堆栈的各种误解之上的。
首先,没有“连接”Tcp或其他概念,越早提出这种区分就越早意识到它与udp或任何其他协议没有区别。
当你的接收超时你有数据要发送或你没有。
如果你不想传输,可以select增加超时时间并重新接收。
如果客户端拔掉了电脑或者断开连接,那么为什么?
这会打破你的“连接”? 也许但不是,谁知道你的机器是如何设置的或者LSP的。
执行此操作的正常方法就像使用其他任何协议一样,我提醒您在出现recive的情况下套接字已经“连接”了。
当您尝试发送该客户端的东西时,您会知道客户端是否“连接”或在TCP下更正确地确认您的数据。 (在udp下也通过icmp根据networking)
为什么你投下的答案是因为你自己的误解。
简而言之,如果您有数据要发送,请确定发送成功或失败后需要执行什么操作。
如果您的接收操作超时相同适用,并且可以select使用0大小的发送来确认对方仍在接收您的数据。