发生WSAEWOULDBLOCK时如何判断链接是否断开

本文探讨了在非阻塞socket环境下如何判断网络连接状态,特别是在recv返回-1且WSAGetLastError返回WSAEWOULDBLOCK时,如何确定是否为对方宕机或网络暂时繁忙。介绍了recv函数在不同情况下的返回值含义,以及如何通过设置超时、使用TCP keep-alive等功能来提前检测连接丢失。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非阻塞socket,recv返回-1,WSAGetLastError返回WSAEWOULDBLOCK,无法判断对方是否宕机(因为网络繁忙的时候或者对方没有发送数据的时候也会返回WSAEWOULDBLOCK),此时需要使用其他方法来判断网络是否断开:
recv() will return 0 upon a graceful disconnect, ie the peer shutdown its end of the connection and its socket stack sent a FIN packet to your socket stack. You are pretty much guaranteed to get this result immediately, whether you use a blocking or non-blocking socket.

recv() will return -1 on any other error, including an abnormal connection loss. You need to use WSAGetLastError() to find out what actually happened. For a lost connection on a blocking socket, you will usually get an error code such as WSAECONNRESET or WSAECONNABORTED. For a lost connection on a non-blocking socket, it is possible that recv() may report an WSAEWOULDBLOCK error immediately and then report the actual error at some later time, maybe via select() with an exception fd_set, or an asynchronous notification, depending on how you implement your non-blocking logic.

However, either way, you are NOT guaranteed to get a failure result on a lost connection in any timely manner! It MAY take some time (seconds, minutes, can even be hours in rare cases) before the OS deems the connection is actually lost and invalidates the socket connection. TCP is designed to recover lost connections when possible, so it has to account for temporary network outages and such, so there are internal timeouts. You don’t see that in your code, it happens in the background.

If you don’t want to wait for the OS to timeout internally, you can always use your own timeout in your code, such as via select(), setsocktopt(SO_RCVTIMEO), TCP keep-alives (setsockopt(SO_KEEPALIVE) or WSAIoCtl(SIO_KEEPALIVE_VALS)), etc. You may still not get a failure immediately, but you will get it sooner rather than later.
参考网址:https://stackoverflow.com/questions/54787490/socket-recv-function
windows系统下,可使用如下方法判断:
bool IsSocketClosed(SOCKET clientSocket)
{
bool ret = false;
HANDLE closeEvent = WSACreateEvent();
WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);

DWORD dwRet = WaitForSingleObject(closeEvent, 0);

if(dwRet == WSA_WAIT_EVENT_0)
ret = true;
else if(dwRet == WSA_WAIT_TIMEOUT)
ret = false;

WSACloseEvent(closeEvent);
return ret;
}
参考:https://blog.youkuaiyun.com/e_wsq/article/details/12878885

判断TCP连接是否断开,可以通过以下方法: ```cpp #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") bool IsTCPDisconnected(SOCKET sock) { char buffer[1]; int result = recv(sock, buffer, sizeof(buffer), MSG_PEEK); if (result == 0) { // 连接已关闭 return true; } else if (result == SOCKET_ERROR) { // 发生错误 int errorCode = WSAGetLastError(); if (errorCode == WSAEWOULDBLOCK || errorCode == WSAECONNRESET) { // 非阻塞套接字返回WSAEWOULDBLOCK,连接被重置返回WSAECONNRESET return true; } } // 连接仍然存在 return false; } int main() { // 初始化Winsock WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return 1; } // 创建套接字并连接到服务器 SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in serverAddress; serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(8080); // 替换为实际的服务器端口号 inet_pton(AF_INET, "127.0.0.1", &(serverAddress.sin_addr)); // 替换为实际的服务器IP地址 if (connect(sock, (sockaddr*)&serverAddress, sizeof(serverAddress)) != 0) { closesocket(sock); WSACleanup(); return 1; } // 使用IsTCPDisconnected函数判断连接是否断开 if (IsTCPDisconnected(sock)) { printf("TCP连接已断开\n"); } else { printf("TCP连接仍然存在\n"); } // 清理资源 closesocket(sock); WSACleanup(); return 0; } ``` 这个示例代码使用了Windows平台下的Winsock库,通过创建套接字并连接到服务器,然后调用`IsTCPDisconnected`函数来判断TCP连接是否断开。`IsTCPDisconnected`函数中使用了`recv`函数的`MSG_PEEK`标志来查看接收缓冲区中的数据,如果返回值为0,则表示连接已关闭;如果返回值为`SOCKET_ERROR`,并且错误码为`WSAEWOULDBLOCK`或`WSAECONNRESET`,则表示连接被重置;否则表示连接仍然存在。注意,在使用Winsock之前,需要调用`WSAStartup`函数来初始化Winsock库,并在使用完后调用`WSACleanup`函数来清理资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值