【发布时间】: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 数据包,以便在尝试发送下一个数据包之前彻底关闭所有内容?
【问题讨论】:
-
文件描述符上的
poll()应返回POLLHUP。 -
看看这个关于 SO 的答案。 stackoverflow.com/questions/14782143/…
标签: c++ c sockets asynchronous tcp