假设我正在运行一个简单的服务器,并从客户端接受了连接。
如何最好地判断客户端已经断开连接?通常,客户端应该发送关闭命令,但如果它手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理这种情况?
假设我正在运行一个简单的服务器,并从客户端接受了连接。
如何最好地判断客户端已经断开连接?通常,客户端应该发送关闭命令,但如果它手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理这种情况?
read()/recv()/recvXXX()
函数读取时返回0。write()/send()/sendXXX()
函数返回-1,同时返回一个值为ECONNRESET
或者某些情况下为'connection timed out' 的errno/WSAGetLastError()
。需要注意的是,后者与“connect timeout”不同,后者可能发生在连接阶段。ioctl()
和FIONREAD
的回答是完全无意义的。它只告诉您目前在套接字接收缓冲区中有多少字节可供读取而不会阻塞。如果客户端五分钟内没有向您发送任何内容,这并不构成断开连接,但这确实会导致FIONREAD
变为零。这两种情况根本不同。进一步解释一下:
如果您在运行服务器,则需要使用TCP_KEEPALIVE监视客户端连接,或自行执行类似的操作,或者对于正在运行连接的数据/协议有知识。
基本上,如果连接被杀死(即未正确关闭),则服务器将不会注意到,直到它试图向客户端写入某些内容,这就是keepalive为您实现的内容。或者,如果您更好地了解协议,也可以在活动超时后断开连接。
如果您正在使用带有完成例程或完成端口的重叠(即异步)I/O,则在客户端关闭连接时(假设您有一个未完成的读取),您将立即收到通知。
unsigned int timeout = 5000; //timeout in ms
if (setsockopt(yourSocket, SOL_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout))<0)
fprintf(stderr,"setsockopt(SO_SNDTIMEO) failed");
这很容易做到:可靠而且不会混乱:
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
我尝试了几种解决方案,但这个似乎是在 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;
}
如果连接丢失,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
,并且不会对缓冲区做任何事情。 - user207421使用设置了读掩码的select将返回已发出信号的句柄,但是当您使用ioctl*检查待读取的字节数时,它将为零。这表明套接字已断开连接。
这是一个关于各种检查客户端是否已断开连接的讨论:Stephen Cleary, Detection of Half-Open (Dropped) Connections。
* 对于Windows,请使用ioctlsocket。