建立多个连接时如何在 C 中设置套接字超时?
- 2024-10-10 09:28:00
- admin 原创
- 75
问题描述:
我正在编写一个简单的程序,它与不同的服务器建立多个连接以进行状态检查。所有这些连接都是按需构建的;最多可以同时创建 10 个连接。我不喜欢每个套接字一个线程的想法,所以我将所有这些客户端套接字都设为非阻塞,并将它们放入 select() 池中。
一切运行良好,直到我的客户抱怨说,当目标服务器停止响应时,他们等待的时间太长,才能够得到错误报告。
我查看了论坛中的几个主题。有人建议可以使用 alarm() 信号或在 select() 函数调用中设置超时。但我处理的是多个连接,而不是一个。当发生进程范围的超时信号时,我无法在所有其他连接中区分超时连接。
有没有办法改变系统默认的超时时间?
解决方案 1:
您可以使用 SO_RCVTIMEO 和 SO_SNDTIMEO 套接字选项来设置任何套接字操作的超时,如下所示:
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof timeout) < 0)
error("setsockopt failed
");
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof timeout) < 0)
error("setsockopt failed
");
编辑:来自setsockopt
手册页:
SO_SNDTIMEO
是为输出操作设置超时值的选项。它接受一个 struct timeval 参数,该参数包含用于限制等待输出操作完成的秒数和微秒数。如果发送操作阻塞了这么长时间,它将返回部分计数,如果没有发送任何数据,则返回错误 EWOULDBLOCK。在当前实现中,每次向协议传送其他数据时都会重新启动此计时器,这意味着限制适用于大小从输出的低水位标记到高水位标记的输出部分。
SO_RCVTIMEO
是一个用于设置输入操作超时值的选项。它接受一个 struct timeval 参数,该参数包含用于限制等待输入操作完成的时间(以秒和微秒为单位)。在当前实现中,每次协议收到其他数据时都会重新启动此计时器,因此该限制实际上是一个不活动计时器。如果接收操作被阻塞了这么长时间而没有收到其他数据,它将返回一个短计数,如果没有收到任何数据,则返回错误 EWOULDBLOCK。struct timeval 参数必须表示正时间间隔;否则,setsockopt() 将返回错误 EDOM。
解决方案 2:
我不确定我是否完全理解了这个问题,但猜测它与我遇到的问题有关,我正在使用 Qt 进行 TCP 套接字通信,全部是非阻塞的,无论是 Windows 还是 Linux。
希望在已连接的客户端发生故障或完全消失时快速收到通知,而不是等待默认的 900 多秒直到断开连接信号发出。实现此操作的技巧是将 SOL_TCP 层的 TCP_USER_TIMEOUT 套接字选项设置为所需值(以毫秒为单位)。
这是一个相对较新的选项,请参阅https://www.rfc-editor.org/rfc/rfc5482,但显然它运行良好,尝试使用 WinXP、Win7/x64 和 Kubuntu 12.04/x64,我选择的 10 秒结果有点长,但比我以前尝试过的任何其他方法都要好得多 ;-)
我遇到的唯一问题是找到正确的包含,因为显然这还没有添加到标准套接字包含中(但是..),所以最后我自己将它们定义如下:
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#endif
#ifndef SOL_TCP
#define SOL_TCP 6 // socket options TCP level
#endif
#ifndef TCP_USER_TIMEOUT
#define TCP_USER_TIMEOUT 18 // how long for loss retry before timeout [ms]
#endif
设置此套接字选项仅当客户端已经连接时才有效,代码行如下:
int timeout = 10000; // user timeout in milliseconds [ms]
setsockopt (fd, SOL_TCP, TCP_USER_TIMEOUT, (char*) &timeout, sizeof (timeout));
并且初始连接的失败被调用 connect() 时启动的计时器捕获,因为 Qt 没有信号,连接信号将不会被引发,因为没有连接,断开连接信号也不会被引发,因为还没有连接。
解决方案 3:
你不能实现自己的超时系统吗?
保留一个超时事件的排序列表,或者更好的是,按照 Heath 的建议,保留一个优先级堆。在 select 或 poll 调用中,使用超时列表顶部的超时值。当该超时到达时,执行与该超时相关的操作。
该操作可能是关闭尚未连接的套接字。
解决方案 4:
connect
超时必须用非阻塞套接字来处理(GNU LibC文档)connect
。您可以connect
立即返回,然后使用select
超时等待连接完成。
这里也解释了这一点:操作正在进行中,连接(函数)出现错误。
int wait_on_sock(int sock, long timeout, int r, int w)
{
struct timeval tv = {0,0};
fd_set fdset;
fd_set *rfds, *wfds;
int n, so_error;
unsigned so_len;
FD_ZERO (&fdset);
FD_SET (sock, &fdset);
tv.tv_sec = timeout;
tv.tv_usec = 0;
TRACES ("wait in progress tv={%ld,%ld} ...
",
tv.tv_sec, tv.tv_usec);
if (r) rfds = &fdset; else rfds = NULL;
if (w) wfds = &fdset; else wfds = NULL;
TEMP_FAILURE_RETRY (n = select (sock+1, rfds, wfds, NULL, &tv));
switch (n) {
case 0:
ERROR ("wait timed out
");
return -errno;
case -1:
ERROR_SYS ("error during wait
");
return -errno;
default:
// select tell us that sock is ready, test it
so_len = sizeof(so_error);
so_error = 0;
getsockopt (sock, SOL_SOCKET, SO_ERROR, &so_error, &so_len);
if (so_error == 0)
return 0;
errno = so_error;
ERROR_SYS ("wait failed
");
return -errno;
}
}
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件