【发布时间】:2018-12-16 05:41:12
【问题描述】:
我有一个简单的服务器,用 C 编写,它接受来自各种来源的传感器和状态信息,然后将其合并并重新格式化为客户端的 ASCII 文本行流。客户端通过侦听器套接字连接,然后读取消息流并对其执行任何操作,直到用户关闭应用程序。由于这是一种单向协议,因此服务器从不费心检查待处理的接收数据。
每当有消息要发送给所有活跃用户时,它都会经过一个简单的循环:
bufflen = strlen(tcp_buff);
for (next_client_ix = 0; next_client_ix < MAX_TCP_CONNECTIONS; next_client_ix++)
if (TCP_client_sd[next_client_ix] != 0)
{
rc = send(TCP_client_sd[next_client_ix], tcp_buff, bufflen, MSG_NOSIGNAL);
if (rc != bufflen)
{
errno_hold = errno;
s = inet_ntoa(Tcp_client_sin[next_client_ix].sin_addr);
remote_port = htons(Tcp_client_sin[next_client_ix].sin_port);
sprintf(log_buff, "Error %d (%s) sending alert to %s:%d. Closing\n", errno_hold, strerror(errno_hold), s, remote_port);
log_message(SB_ALERT_TEXT_ERROR, log_buff);
close(TCP_client_sd[next_client_ix]);
TCP_client_sd[next_client_ix] = 0; // Free the socket for the next client
}
}
多年来,这在 Ubuntu 10.04 到 16.04 版本上运行良好,当时我们通常一次只有 1 或 2 个(偶尔 3 个)客户端处于活动状态,所有客户端都通过以太网 LAN 连接。最近,我们一直在一次运行更多的客户端(仍然是个位数),并且大部分增加是 Windows 客户端的副本,通常通过 SOHO WiFi 路由器连接到 LAN。上个月,当我们有一个客户端通过公共 WiFi 从会议厅远程连接时,这种情况也出现了。
每两周一次,服务器会停止向所有客户端发送几分钟。当我使用 netstat 进行调查时,我发现一个或(通常)多个套接字卡在 CLOSE_WAIT 中,Recv-Q 为 1,Send-Q 中大约有 13K。最终,服务器吐出一条错误消息,指出由于 errno 为 32(Broken Pipe),它正在关闭客户端连接,然后一切恢复正常。
我猜测 Windows-via-WiFi 连接中存在一些怪癖,导致连接关闭顺序发生不同,但这是一个不太受过教育的猜测。
我的问题(终于!)是我应该做些什么来在问题变成服务器挂起之前检测到即将到来的问题,或者让 Linux 立即给我一个错误,而不是让我在它决定放弃时等待。对于期望从客户端传入数据的服务器,我发现了各种想法,但对于“只写”连接却没有(嗯,一个答案是在每次写入之前运行 netstat 并分析其输出,但这对于我们希望该系统在全面生产时能够将来自数百个传感器阵列的数据提供给数十个客户)。我已经尝试添加一些代码来尝试使用仅限 Linux 的 SIOCOUTQ fcntl 来检测它,以查找在传输队列中堆积的数据,但由于它在野外很少发生,所以无法进行良好的测试。我尝试创建一个行为不端的客户端并不顺利,因为客户端 Linux 很高兴地在其接收队列中堆积了足够的数据,以防止它在几天内失败。因此,服务器永远不会在其一侧看到堆积物。
是否有一些我错过的套接字或 API 调用选项会显示“忘记耐心并重试:现在放弃并失败!”?我是否应该耐心等待几周,看看我的 SIOCOUTQ 修复是否解决了问题?还是我需要改进我的 google 关键字选择技巧才能找到迄今为止我一直无法找到的答案?
谢谢,
跑
【问题讨论】:
-
我有一个挂起的“堆”CLOSE_WAIT 连接,但最近的事件只有一个。这使混乱更加复杂:如果写入单个“未准备好接收更多数据”套接字会导致挂起,我是如何设法在 CLOSE_WAIT 中获得 8 个套接字的?
标签: linux sockets server freeze