TCP连接,如果server端所在机器突然掉电,client是如何以及多久感知tcp连接断开

发布于:2024-07-04 ⋅ 阅读:(10) ⋅ 点赞:(0)

TCP连接,如果server端所在机器突然掉电,client是如何以及多久感知tcp连接断开

1. 场景:

TCP连接,client 和 server已经成功建立连接,这时server端所在机器突然掉电且且后续一直处于掉电状态。在没有任何应用层心跳协议的场景下,client端如何以及多久感知tcp连接断开?

这里得区分两个场景:

  1. client和server建立连接后,client一直没有发送数据给server,也就是一直没有数据交互;
  2. client和server建立连接后,client一直有数据发送给server,有数据交互。

1.1 client没有发数据给server场景

没有发数据的场景下,会触发tcp的保活机制,也就是TCP keepalive。

1.1.1 TCP keepalive原理

TCP Keepalive 是一种用于检测空闲连接是否仍然存在的机制。它通过周期性地发送探测数据包来验证对端是否还活着。如果对端没有回应,TCP连接会被认为已经失效,并最终关闭。下面是 TCP Keepalive 的触发机制和检测方法:
触发机制
TCP Keepalive 主要包括三个参数:

  1. tcp_keepalive_time:
  • 空闲时间:在没有任何数据传输后,连接保持空闲的时间(秒),默认值通常为 7200 秒(2 小时)。
  1. tcp_keepalive_intvl:
  • 重试间隔:发送 Keepalive 探测包之间的间隔时间(秒),默认值通常为 75 秒。
  1. tcp_keepalive_probes:
  • 探测次数:在关闭连接之前发送 Keepalive 探测包的最大次数,默认值通常为 9 次。

Keepalive 流程

  1. 空闲时间达到(tcp_keepalive_time):
  • 当 TCP 连接空闲达到 tcp_keepalive_time 后,开始发送 Keepalive 探测包。
  1. 发送探测包:
  • 每隔 tcp_keepalive_intvl 秒发送一个 Keepalive 探测包,探测包是一个长度为零的 TCP 数据包,包含当前序列号。
  1. 等待响应:
  • 如果对端在 tcp_keepalive_intvl 时间内没有回应,则重发探测包。
  1. 达到探测次数(tcp_keepalive_probes):
  • 如果连续发送 tcp_keepalive_probes 次探测包后仍未收到对端的回应,内核认为连接已失效,关闭连接。

所以,此场景下,在linux系统中,最少经过7875s,client才可以感知到tcp连接断开。

7200 + (75 * 9) = 7875 s

并且应用程序需要通过socket接口设置SO_KEEPALIVE选项才能够使能TCP keepalive功能。
如下所示:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <unistd.h>

int set_keepalive(int sockfd) {
    int optval = 1;
    socklen_t optlen = sizeof(optval);

    // Enable keepalive
    if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
        return -1;
    }

    // Set keepalive time
    optval = 600; // 10 minutes
    if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen) < 0) {
        return -1;
    }

    // Set keepalive interval
    optval = 60; // 1 minute
    if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen) < 0) {
        return -1;
    }

    // Set keepalive probes
    optval = 5; // max 5 probes
    if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen) < 0) {
        return -1;
    }

    return 0;
}

1.2 client一直发数据给server场景

此场景下,client会一直重传报文,直到某个上限后,内核就会认为tcp连接断开,client就能感知到此tcp连接断开。

那client重传几次呢?重传多久呢?
这里收两个参数的限制:

  1. tcp_retries1
  2. tcp_retries2

1.2.1初期重传尝试(tcp_retries1):

  • 在最初尝试发送数据包并且没有收到ACK确认时,TCP将根据Retransmission Timeout (RTO)计算出的时间间隔来进行重传。如果重传次数达到tcp_retries1定义的值,客户端不会立即关闭连接,而是进入下一阶段的重传。

1.2.2 后期重传尝试(tcp_retries2):

  • 如果在达到tcp_retries1的重传次数之后,连接仍然没有得到响应,TCP将继续尝试重传数据包。如果重传次数达到tcp_retries2定义的值,TCP将认为连接出现了硬错误,此时,连接将被认定为失效,TCP将关闭连接并通知应用程序发生了错误(通常是ETIMEDOUT)。

1.2.3 计算重传时间

假设:
tcp_retries1 = 3
tcp_retries2 = 15
RTO = 1
那么总的重传次数为 tcp_retries2,即 15 次,而不是 3 次初始重传加上 15 次长期重传。换句话说,重传的总次数是 15 次,其中前 3 次由 tcp_retries1 控制。

重传过程

  1. 初始重传阶段:
  • 第1次重传(初始RTO):等待1秒
  • 第2次重传:等待2秒
  • 第3次重传:等待4秒
  1. 长期重传阶段:
  • 第4次重传:等待8秒
  • 第5次重传:等待16秒

    最后一次重传(第15次):等待TCP_RTO_MAX(通常为120秒)
    注意: 这里每次重传时间最大值就是TCP_RTO_MAX。
    总的重传时间:
7秒(初始阶段) + 1080秒(长期阶段) = 1087秒(约18分钟)