在使用C/C 的异步插座时,检测远程断开(半粘液)

Detect remote disconnect (half-close) when using asynchronous sockets in C/C++

本文关键字:程断开 粘液 检测 断开 异步 插座      更新时间:2023-10-16

在使用c/c

中使用异步插座时检测远程断开(半连接)

我使用以异步模式(fcntl(sock, F_SETFL, O_ASYNC);)运行的C插座实现了TCP/IP客户端,以最大程度地减少阻止。客户端将仅将数据发送到服务器,并且永远不会收到任何数据。因此,循环看起来像这样:

while (true) {
    ...
    fd_set writefds;
    FD_ZERO(&writefds);
    FD_SET(socketFd, &writefds);
    select(socketFd + 1, NULL, &writefds, NULL, &selectTimeout);
    if (FD_ISSET(socketFd, &writefds)) {
       ssize_t cBytes = send(socketFd, someBuffer, someSize, 0);
       ...
    }
    ...
 }

如何检测服务器是否断开连接?正如此问题所建议的那样,最初合理的解决方案是调用recv(socketFd, &buf, len, MSG_PEEK);并检查该调用是否返回0。但是,这显然不起作用,因为首先,服务器实际上并未在我的情况下发送任何数据。其次,在广义的情况下,服务器可能不会一直发送数据,因此recv()因此会返回0,因为它是非阻滞的。

使用tcpdump监视服务器与客户端之间的连接时,服务器关闭连接时会发送一个鳍数据包,但客户端不承认这一点。我无法使用recv()send()select()识别它的半粘液。实际上,第一个迹象表明问题是错误的是服务器发送rst时客户端试图将数据发送到现在关闭的连接时:

IP6 ::1.62179 > ::1.2526: Flags [S], seq ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [S.], seq ..., length 0
IP6 ::1.62179 > ::1.2526: Flags [.], ack 1, ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [.], ack 1, ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [F.], seq 1, ..., length 0
IP6 ::1.62179 > ::1.2526: Flags [.], ack 2, ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [.], ack 1, ..., length 0
IP6 ::1.62179 > ::1.2526: Flags [P.], seq 1:7, ack 2, ..., length 6
IP6 ::1.2526 > ::1.62179: Flags [R], seq ..., length 0

如何确定服务器已经发送了鳍数据包,以便在尝试发送下一个数据包之前可以清洁关闭所有内容?

解决此问题的解决方案是使用sys/poll.h中定义的poll()poll()如果在插座上收到鳍片,将在pollfd结构中设置POLLHUP位:

#include <sys/poll.h>
...
pollfd ufds[1];
ufds[0].fd = socketFd;
ufds[0].events = POLLIN;
int pollRes = poll(ufds, 1, 0);
if (pollRes == 1) {
    if (ufds[0].revents & POLLHUP) {
        std::cout << "Got FIN, closing socket." << std::endl;
        shutdown(socketFd, SHUT_RDWR);
        close(socketFd);
        ...
    }
}

服务器可能不会始终发送数据,因此Recv()因此将返回0,因为它是非阻滞的。

否。如果您在活动连接上调用它,则recv将不会返回0。如果发生错误,它将返回读取或-1的字节数(检查errno)。它仅在同行执行有序关闭检查的男人页面

时才返回0

如何确定服务器已经发送了鳍数据包,以便在尝试发送下一个数据包之前可以清洁关闭所有内容?

您的代码应执行recv,确定下一个操作的返回值。返回0表示对等方已关闭连接。