[我之前问过类似的问题。这是一个更集中的版本。]
什么会导致服务器对 TCP 套接字的 select() 调用始终超时,而不是“看到”客户端的套接字 close()?在客户端,套接字是一个常规的 socket() 创建的阻塞套接字,它成功连接到服务器并成功传输往返事务。在服务器端,套接字通过accept() 调用创建,处于阻塞状态,通过fork() 传递给子服务器进程,被顶级服务器关闭,并被子服务器进程成功使用初始交易。当客户端随后关闭套接字时,子服务器进程的 select() 调用始终超时(1 分钟后),而不是指示套接字上的读取就绪状态。select() 调用仅查找准备就绪的条件:
这是子服务器进程中使用 select() 的简化但逻辑等效的代码:
int one_svc_run(
const int sock,
const unsigned timeout)
{
struct timeval timeo;
fd_set fds;
timeo.tv_sec = timeout;
timeo.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock, &fds);
for (;;) {
fd_set readFds = fds;
int status = select(sock+1, &readFds, 0, 0, &timeo);
if (status < 0)
return errno;
if (status == 0)
return ETIMEDOUT;
/* This code not reached when client closes socket */
/* The time-out structure, "timeo", is appropriately reset here */
...
}
...
}
这是客户端事件序列的逻辑等价物(未显示错误处理):
struct sockaddr_in *raddr = ...;
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
(void)bindresvport(sock, (struct sockaddr_in *)0);
connect(sock, (struct sockaddr *)raddr, sizeof(*raddr));
/* Send a message to the server and receive a reply */
(void)close(sock);
fork()、exec() 和 system() 永远不会被调用。代码比这复杂得多,但这是相关调用的顺序。
Nagel 的算法会导致 FIN 数据包在 close() 时不发送吗?