套接字保持活动功能无法正常工作

9

我有一个客户端连接到一个服务器,在空闲时,它会在几个小时后超时。 我添加了 setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE...) 每秒设置一次 但这似乎没有任何作用。 为什么keepalive不起作用?有什么线索吗? 如果我使用SOL_TCP而不是SOL_SOCKET,是否会有所不同? 这是在Linux上进行的。


定义“几个小时后超时”,具体表现是什么? - user207421
错误号110 - 连接超时。我进行了tcpdump,但没有看到keepalive消息。 - excalibur
设置 TCP_USER_TIMEOUT 选项可能会解决问题。 - Homaei
2个回答

13
int val = 1;
setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof val)

只需启用Keepalives。您将获得保持活动探测的默认计时器,您可以使用以下命令查看:

sysctl net.ipv4.tcp_keepalive_time

通常,默认时间是几个小时。

如果您想更改默认计时器,可以使用以下内容:

struct KeepConfig cfg = { 60, 5, 5};
set_tcp_keepalive_cfg(fd, &cfg);

使用这里的辅助函数:

struct KeepConfig {
    /** The time (in seconds) the connection needs to remain 
     * idle before TCP starts sending keepalive probes (TCP_KEEPIDLE socket option)
     */
    int keepidle;
    /** The maximum number of keepalive probes TCP should 
     * send before dropping the connection. (TCP_KEEPCNT socket option)
     */
    int keepcnt;

    /** The time (in seconds) between individual keepalive probes.
     *  (TCP_KEEPINTVL socket option)
     */
    int keepintvl;
};

/**
* enable TCP keepalive on the socket
* @param fd file descriptor
* @return 0 on success -1 on failure
*/
int set_tcp_keepalive(int sockfd)
{
    int optval = 1;

    return setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}

/** Set the keepalive options on the socket
* This also enables TCP keepalive on the socket
*
* @param fd file descriptor
* @param fd file descriptor
* @return 0 on success -1 on failure
*/
int set_tcp_keepalive_cfg(int sockfd, const struct KeepConfig *cfg)
{
    int rc;

    //first turn on keepalive
    rc = set_tcp_keepalive(sockfd);
    if (rc != 0) {
        return rc;
    }

    //set the keepalive options
    rc = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &cfg->keepcnt, sizeof cfg->keepcnt);
    if (rc != 0) {
        return rc;
    }

    rc = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &cfg->keepidle, sizeof cfg->keepidle);
    if (rc != 0) {
        return rc;
    }

    rc = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &cfg->keepintvl, sizeof cfg->keepintvl);
    if (rc != 0) {
        return rc;
    }

    return 0;
}

这正是我在寻找的。谢谢。 - excalibur
非常感谢,这救了我的一天。像“聊天”应用程序这样的应用程序是否有规定的超时时间?如果您描述这些方法的内部操作,将会很有帮助。它们是否向服务器套接字发送0有效负载数据包?这是昂贵的操作吗(例如,可能会创建一个新线程)?顺便说一句,@excalibur,如果您满意,请通过点击下面的正确标记接受此答案。 - iammilind

4

尽管它的名称是keep-alive,但它并不是用于保持连接的活跃性,而是用于定期交换数据包,以确保对等方之间存在网络路径。它会终止那些在空闲时可以幸存于长时间网络中断的连接。

由于这种行为,除非有充分的理由(如telnet或SSH连接,在客户端无法访问时结束会话),否则不应使用keep-alive。

很可能是服务器根据某些连接处理策略,在n小时后关闭连接,无论keepalive是否使用。

更新:Dror Harari的评论增加了这种情况的另一个问题:NAT中间路由器。完全空闲的连接将被它们关闭,而主机却认为它们仍然具有连接,实际上已经没有了。使用一些形式的keepalive(无论是在TCP还是应用程序协议级别)是一种有效的解决方法。


不,服务器没有关闭它。连接只有在空闲时才会超时。保持活动状态的目的是使其看起来“非空闲”-这就是没有起作用的地方。 - excalibur
也许是一些中间路由器/防火墙/NAT建立了连接超时。在Linux中,常见的NAT超时时间为1小时(3600秒)。 - epx
1
当像防火墙/NAT这样的网络中介决定终止空闲连接时,它通常会在不通知连接对等方连接已经断开(例如没有FIN数据包)的情况下将其简单地消除。因此,从对等方的角度来看,连接是活动的,他们唯一能够发现套接字已经消失的方法是使用读取超时。通过保持活动机制,TCP/IP堆栈将检测到断开连接,因此如果适当降低空闲时间设置,则应用程序将更早地收到有关断开连接的通知。 - Dror Harari
1
@DrorHarari 感谢您的评论,我已根据此修改了我的答案。 - epx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接