【发布时间】:2013-02-23 02:58:58
【问题描述】:
[我之前问过类似的问题。这是一个更专注的版本。]
什么会导致服务器对 TCP 套接字的 select() 调用始终超时,而不是“看到”客户端的套接字 close()?在客户端,套接字是一个常规的 socket() 创建的阻塞套接字,它成功连接到服务器并成功传输往返事务。在服务器端,套接字通过accept()调用创建,处于阻塞状态,通过fork()传递给子服务器进程,被顶级服务器关闭,并被子服务器进程成功使用初始交易。当客户端随后关闭套接字时,子服务器进程的 select() 调用始终超时(1 分钟后),而不是指示套接字上的读取就绪状态。 select() 调用仅查找可读取条件:可写入和异常参数为 NULL。
这是在子服务器进程中使用 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() 时无法发送?
【问题讨论】:
-
可能有防火墙、nat 网关或其他设备跟踪和超时连接,这将导致您的服务器看不到 close()。如果您可以排除这种情况,并且这种情况始终如一地发生而不是由网络问题引起,那么您的代码在某处存在错误。
-
你是
close()-ing 服务器端父进程中的accept()-ed 套接字吗? -
@NikolaiNFetissov 是的
-
@nos 我认为 NAT、防火墙或路由器可能会持续删除 FIN 数据包,但不太可能。我已经仔细而详尽地检查了代码(不幸的是,它太大而无法发布)。我认为更有可能是我不理解某些东西。
-
只是对您最后一个问题的评论,nagel 不会导致不发送 fin(或 rst)。另外
(void)close(socket);是一个相当大的错误,希望这只是一个错字。