如何在进行多个连接时在C中设置套接字超时?

Ric*_*Liu 61 c sockets linux

我正在写一个简单的程序,它可以与不同的服务器建立多个连接以进行状态检查.所有这些连接都是按需构建的; 最多可同时创建10个连接.我不喜欢单线程每插槽的想法,所以我将所有这些客户端套接字非阻塞,并将它们扔进select()池.

它工作得很好,直到我的客户抱怨等待时间太久才能在目标服务器停止响应时获得错误报告.

我已在论坛中查看了几个主题.有人建议可以使用alarm()信号或在select()函数调用中设置超时.但我正在处理多个连接,而不是一个连接.当进程范围超时信号发生时,我无法区分所有其他连接之间的超时连接.

无论如何都要更改系统默认超时持续时间?

Tob*_*oby 111

您可以使用SO_RCVTIMEO和SO_SNDTIMEO套接字选项为任何套接字操作设置超时,如下所示:

    struct timeval timeout;      
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
                sizeof(timeout)) < 0)
        error("setsockopt failed\n");

    if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
                sizeof(timeout)) < 0)
        error("setsockopt failed\n");
Run Code Online (Sandbox Code Playgroud)

编辑:setsockopt 手册页:

SO_SNDTIMEO是一个为输出操作设置超时值的选项.它接受struct timeval参数,其中包含用于限制输出操作完成等待的秒数和微秒数.如果发送操作在这么长时间内被阻塞,则返回部分计数或如果没有数据发送则返回错误EWOULDBLOCK.在当前实现中,每次将附加数据传递到协议时重新启动该计时器,这意味着该限制适用于从低水位标记到高水位标记的输出部分,用于输出.

SO_RCVTIMEO是一个为输入操作设置超时值的选项.它接受struct timeval参数,其中包含用于限制输入操作完成等待的秒数和微秒数.在当前实现中,每当协议接收到附加数据时重新启动该计时器,因此该限制实际上是不活动计时器.如果接收操作在这段时间内被阻止而没有接收到其他数据,则它会返回一个短计数或如果没有收到数据则返回错误EWOULDBLOCK.struct timeval参数必须表示正时间间隔; 否则,setsockopt()返回错误EDOM.

  • 1.这不适用于`connect()`或通常用于任何套接字操作'.它分别适用于阅读和写作.2.我无法看到这个答案在非阻塞模式下是如何正确的,这是OP所要求的.正确的答案是使用select()超时并跟踪哪些套接字在哪些时间启动了连接. (6认同)
  • 这个_does_适用于`connect()`.请参阅[socket(7):Linux联机帮助页]中的`SO_RCVTIMEO`和`SO_SNDTIMEO`(http://linux.die.net/man/7/socket),其中说"如果没有数据传输且超时已经到达然后返回-1,并将errno设置为`EAGAIN`或`EWOULDBLOCK`,**或`EINPROGRESS`(用于`connect(2)`)**就好像套接字被指定为非阻塞一样" (4认同)
  • 你确定这适用于connect()吗?我不相信. (2认同)
  • 根据这个http://pubs.opengroup.org/onlinepubs/009695399/functions/connect.html,"如果无法立即建立连接并且没有为套接字的文件描述符设置O_NONBLOCK,则connect()将阻止在建立连接之前,直到未指定的超时间隔." 看起来这些超时间隔仅用于读取或写入操作. (2认同)
  • @CraigMcQueen,您的选择性引用的句子开始于'如果输入或输出函数在这段时间内阻塞,并且数据已发送或接收,则该函数的返回值将是传输的数据量;...' `connect()` 不是输入或输出函数,并且不返回计数。关于“connect()”的评论似乎是一个无关紧要的题外话。您需要“select()”来使连接超时时间短于平台默认值。 (2认同)
  • 在 Linux 4.13 上,只有 `SO_SNDTIMEO` 设置了 `connect()` 超时,`SO_RCVTIMEO` 似乎被忽略了。 (2认同)

wgr*_*wgr 13

我不确定我是否完全理解这个问题,但猜测它与我的问题有关,我正在使用Qt与TCP套接字通信,所有非阻塞,Windows和Linux.

想要在已连接的客户端发生故障或完全消失时获得快速通知,并且在断开信号被提升之前不等待默认的900+秒.实现这一目标的技巧是将SOL_TCP层的TCP_USER_TIMEOUT套接字选项设置为所需的值,以毫秒为单位.

这是一个比较新的选择,请参阅http://tools.ietf.org/html/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
Run Code Online (Sandbox Code Playgroud)

设置此套接字选项仅在客户端已连接时才有效,代码行如下所示:

int timeout = 10000;  // user timeout in milliseconds [ms]
setsockopt (fd, SOL_TCP, TCP_USER_TIMEOUT, (char*) &timeout, sizeof (timeout));
Run Code Online (Sandbox Code Playgroud)

并且在调用connect()时启动的定时器捕获初始连接的失败,因为没有Qt的信号,连接信号将不会被提升,因为没有连接,并且断开信号将也没有被提出,因为还没有连接..


Zan*_*ynx 9

你不能实现自己的超时系统吗?

保留一个排序列表,或者更好的是Heath建议的优先级堆超时事件.在select或poll调用中,使用超时列表顶部的超时值.当超时到达时,请执行附加到该超时的操作.

该操作可能正在关闭尚未连接的套接字.


小智 5

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} ...\n",
            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\n");
        return -errno;
    case -1:
        ERROR_SYS ("error during wait\n");
        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\n");
        return -errno;
    }
}
Run Code Online (Sandbox Code Playgroud)