【问题标题】:Detect remote disconnect (half-close) when using asynchronous sockets in C/C++在 C/C++ 中使用异步套接字时检测远程断开连接(半关闭)
【发布时间】:2017-05-08 03:30:00
【问题描述】:

在 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);
       ...
    }
    ...
 }

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

当使用 tcpdump 监控服务器和客户端之间的连接时,服务器在关闭连接时会发送一个 FIN 数据包,但客户端不承认这一点。我无法使用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

如何确定服务器已发送 FIN 数据包,以便在尝试发送下一个数据包之前彻底关闭所有内容?

【问题讨论】:

标签: c++ c sockets asynchronous tcp


【解决方案1】:

服务器可能不会一直发送数据,因此 recv() 无论如何都会返回 0,因为它是非阻塞的。

没有。如果您在活动连接上调用recv,它将不会返回 0。它返回读取的字节数,如果发生错误,则返回 -1(检查 errno)。仅当对端执行了有序关闭时才返回 0 check the man page

如何确定服务器已发送 FIN 数据包,以便在尝试发送下一个数据包之前彻底关闭所有设备?

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

【讨论】:

  • 再次查看我的代码,我发现了这个解决方案不起作用的原因。我将所有对recv() 的调用隐藏在对select() 的调用之后,因为从来没有任何数据可以接收recv() 从未真正被调用过。因此,这种情况下的解决方案是定期调用recv(),而不管select() 的结果如何,并在recv() 返回错误(-1)时忽略它,但在没有收到错误时关闭套接字(0)。跨度>
  • 对于非阻塞套接字,您需要依赖selectpollepoll 等机制。根据事件采取进一步行动。您无需定期致电recv。让轮询机制将套接字事件通知给应用程序
【解决方案2】:

解决此问题的方法是使用poll() 中定义的sys/poll.h。如果在套接字上收到了 FIN,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);
        ...
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-04
    • 2019-06-23
    • 2011-09-18
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    相关资源
    最近更新 更多