You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ WinSock技术问题:如何检测接收端已断开连接?

检测TCP对端异常断开的解决方案

先解决WSAPoll的10022错误

错误码10022对应WSAEINVAL(无效参数),你的代码问题出在events参数设置上:POLLHUP、POLLERR属于TCP异常事件,不需要在events中指定——无论是否设置,只要事件发生,都会在revents中返回。你只需指定实际要监听的事件(比如POLLOUT用于检测可写)即可。

正确检测对端断开的方法

当对端进程异常终止时,操作系统会自动关闭其持有的socket并发送FIN包,但本地TCP连接会进入CLOSE_WAIT状态,此时send()可能仍能成功(因为数据只是写入本地缓冲区),需要通过以下方式检测:

1. 修正WSAPoll的使用逻辑

调整参数后,通过检查revents中的异常事件判断连接状态:

WSAPOLLFD pollfds[1];
pollfds[0].fd = sockfd;
pollfds[0].events = POLLOUT; // 仅监听可写事件,异常事件自动返回

// 设置超时时间,避免无限阻塞,示例为3000毫秒
int ready = WSAPoll(pollfds, 1, 3000);
if (ready == -1) {
    int error = WSAGetLastError();
    std::cout << "Poll错误!错误码: " << error << std::endl;
    isConnected = false;
} else if (ready > 0) {
    // 检查是否存在断开或错误事件
    if (pollfds[0].revents & (POLLHUP | POLLERR | POLLRDHUP)) {
        isConnected = false;
    } else if (pollfds[0].revents & POLLOUT) {
        // 执行发送操作
        ssize_t sent = send(sockfd, buffer, len, 0);
        if (sent == -1) {
            int error = WSAGetLastError();
            // 连接重置或关闭错误
            if (error == WSAECONNRESET || error == WSAESHUTDOWN) {
                isConnected = false;
            }
        }
    }
}

2. 发送后用getsockopt检测socket状态

即使send()返回成功,也可以通过getsockopt查询socket的隐藏错误:

ssize_t sent = send(sockfd, buffer, len, 0);
if (sent == -1) {
    isConnected = false;
} else {
    int error = 0;
    socklen_t len = sizeof(error);
    // 查询socket的错误状态
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) == 0 && error != 0) {
        isConnected = false;
    }
}

3. 尝试接收数据(即使仅发送)

当对端发送FIN包后,本地调用recv()会返回0(表示连接关闭)。可以用MSG_PEEK参数不读取数据,仅检测状态:

char dummy[1];
ssize_t recvRet = recv(sockfd, dummy, sizeof(dummy), MSG_PEEK);
if (recvRet == 0) {
    // 对端已断开
    isConnected = false;
} else if (recvRet == -1) {
    int error = WSAGetLastError();
    // 非阻塞模式下的EAGAIN/EWOULDBLOCK是正常的,其他错误则表示断开
    if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS) {
        isConnected = false;
    }
}

4. 启用TCP KeepAlive(推荐)

通过系统层面的TCP保活机制,定期探测对端是否存活,即使没有数据传输也能及时检测到断开:

BOOL keepAlive = TRUE;
DWORD keepIdle = 5000;    // 连接空闲5秒后开始发送探测包
DWORD keepInterval = 1000; // 每隔1秒发送一次探测包
DWORD keepCount = 3;       // 连续3次探测失败则判定连接断开

// 设置保活选项
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&keepAlive, sizeof(keepAlive));
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&keepIdle, sizeof(keepIdle));
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&keepInterval, sizeof(keepInterval));
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, (char*)&keepCount, sizeof(keepCount));

启用后,当对端异常终止,系统会在探测失败后标记连接为错误状态,后续的send/recv操作会直接返回错误。

为什么send()没返回错误?

TCP的send()成功仅代表数据被写入本地发送缓冲区,由协议栈负责后续传输。当对端进程终止后,本地TCP连接处于CLOSE_WAIT状态,发送缓冲区仍可接收数据,直到缓冲区被填满,send()才会阻塞(阻塞socket)或返回错误(非阻塞socket)。因此仅靠send()无法及时检测到对端断开。

内容的提问来源于stack exchange,提问作者Edgar Khachatryan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 21:24:53