假设我正在运行一个简单的服务器,并accept()
从客户端建立了连接。
告诉客户端何时断开连接的最佳方法是什么?通常,客户端应该发送关闭命令,但是如果客户端手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理?
假设我正在运行一个简单的服务器,并accept()
从客户端建立了连接。
告诉客户端何时断开连接的最佳方法是什么?通常,客户端应该发送关闭命令,但是如果客户端手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理?
Answers:
select(设置了读取掩码)将返回带有句柄的信号,但是当您使用ioctl *检查待读取的字节数时,它将为零。这表明套接字已断开连接。
这是有关检查客户端已断开连接的各种方法的精彩讨论:Stephen Cleary,检测到半开(断开)连接。
*对于Windows,请使用ioctlsocket。
在TCP中,只有一种方法可以检测到有序的断开连接,即从read()/recv()/recvXXX()
读取时获取零作为返回值。
也只有一种可靠的方法来检测断开的连接:通过对其进行写入。在对断开的连接进行足够的写入之后,TCP将进行足够的重试和超时以知道它已断开,并最终导致write()/send()/sendXXX()
返回errno/WSAGetLastError()
值-1ECONNRESET,
或在某些情况下“连接超时”的-1 。请注意,后者不同于“连接超时”,后者可能发生在连接阶段。
您还应该设置一个合理的读取超时,并丢弃失败的连接。
这里的答案是ioctl()
和FIONREAD
胡说八道。所要做的就是告诉您套接字接收缓冲区中当前有多少字节,可以无阻塞地读取。如果客户五分钟内未向您发送任何东西,这并不构成断开连接,但确实会导致断开连接FIONREAD
。不一样的东西:甚至都不接近。
要进一步扩展:
如果您正在运行服务器,则需要使用TCP_KEEPALIVE来监视客户端连接,或者自己进行类似的操作,或者具有有关通过连接运行的数据/协议的知识。
基本上,如果连接被终止(即未正确关闭),则服务器将不会注意到它,直到尝试向客户端写入内容,这就是keepalive为您实现的。或者,如果您更好地了解协议,则无论如何都可以在不活动超时时断开连接。
Timeout
尝试寻找EPOLLHUP或EPOLLERR。 如何检查客户端连接是否仍然有效
在某些情况下,但并非所有情况下,都可以读取并查找0。
TCP在协议中具有“打开”和“关闭”过程。一旦“打开”,将保持连接直到“关闭”。但是有很多事情可以异常阻止数据流。就是说,确定是否可能使用链接的技术高度依赖于协议和应用程序之间的软件层。上面提到的那些重点是试图以非侵入性方式(读或写0字节)使用套接字的程序员,这可能是最常见的。库中的某些层将为程序员提供“轮询”。例如,Win32异步(延迟)调用可以启动读取操作,该读取操作将返回无错误且0字节的信号,以指示无法再读取的套接字(大概是TCP FIN过程)。其他环境可能使用“事件” 如其包装层中所定义。这个问题没有单一答案。检测何时无法使用套接字并应将其关闭的机制取决于库中提供的包装器。还要注意的是,套接字本身可以被应用程序库下的各层重用,因此明智的做法是弄清楚您的环境如何与Berkley Sockets接口打交道。
"""
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()
如果连接丢失,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();
}
}
errno == ECONNRESET
,并且对缓冲区完全不做任何事情。