0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么要使用 TCP keepalive

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-13 11:28 次阅读

为了理解 TCP keepalive的作用。我们需要清楚,当TCP的Peer A ,Peer B 两端建立了连接之后,如果一端突然拔掉网线或拔掉电源时,怎么检测到拔掉网线或者拔掉电源、链路不通?原因是在需要长连接的网络通信程序中,经常需要心跳检测机制,来实现检测对方是否在线或者维持网络连接的需要。

什么是 TCP 保活?

当你建立一个 TCP 连接时,你关联了一组定时器。其中一些计时器处理保活过程。当保活计时器达到零时,向对等方发送一个保活探测数据包,其中没有数据并且 ACK 标志打开。

由于 TCP/IP 规范,可以这样做,作为一种重复的 ACK,并且远程端点将没有参数,因为 TCP 是面向流的协议。另一方面,将收到来自远程主机的回复,没有数据和ACK 集。

如果收到对 keepalive 探测的回复,则可以断言连接仍在运行。事实上,TCP 允许处理流,而不是数据包,因此零长度数据包对用户程序没有危险。

此过程很有用,因为如果其他对等方失去连接(例如通过重新启动),即使没有流量,也会注意到连接已断开。如果对等方未回复 keepalive 探测,可以断言连接不能被视为有效,然后采取正确的操作。

为什么要使用 TCP keepalive?

1、检查死节点 2、 防止因网络不活动而断开连接

检查死节点

想一想 Peer A 和 Peer B 之间的简单 TCP 连接:初始的三次握手,从 A 到 B 的一个 SYN 段,从 B 到 A 的 SYN/ACK,以及从 A 到 B 的最终 ACK。

图片

此时,我们处于稳定状态:连接已建立,现在我们通常会等待有人通过通道发送数据。

那么问题来了:从 B 上拔下电源,它会立即断电,而不会通过网络发送任何信息来通知 A 连接将断开。

从它的角度来看,A 已准备好接收数据,并且不知道 B 已经崩溃。现在恢复B的电源,等待系统重启。A 和 B 现在又回来了,但是当 A 知道与 B 仍然处于活动状态的连接时,B 不知道。当 A 尝试通过死连接向 B 发送数据时,情况自行解决,B 回复 RST 数据包,导致 A 最终关闭连接。

_____                                                     _____
   |     |                                                   |     |
   |  A  |                                                   |  B  |
   |_____|                                                   |_____|
      ^                                                         ^
      |--- >--- >--- >-------------- SYN -------------- >--- >--- >---|
      |---< ---< ---< ------------ SYN/ACK ------------< ---< ---< ---|
      |--- >--- >--- >-------------- ACK -------------- >--- >--- >---|
      |                                                         |
      |                                       system crash --- > X
      |
      |                                     system restart --- > ^
      |                                                         |
      |--- >--- >--- >-------------- PSH -------------- >--- >--- >---|
      |---< ---< ---< -------------- RST --------------< ---< ---< ---|
      |                                                         |

Keepalive 可以告诉您何时无法访问另一个对等点,而不会出现误报的风险。

防止因网络不活动而断开连接

keepalive 的另一个有用目标是防止不活动断开通道。当你在 NAT 代理或防火墙后面时,无缘无故断开连接是一个非常常见的问题。这种行为是由代理和防火墙中实现的连接跟踪过程引起的,它们跟踪通过它们的所有连接。

它们跟踪通过它们的所有连接。由于这些机器的物理限制,它们只能在内存中保留有限数量的连接。最常见和合乎逻辑的策略是保持最新的连接并首先丢弃旧的和不活动的连接。

_____           _____                                     _____
   |     |         |     |                                   |     |
   |  A  |         | NAT |                                   |  B  |
   |_____|         |_____|                                   |_____|
      ^               ^                                         ^
      |--- >--- >--- >---|----------- SYN ------------- >--- >--- >---|
      |---< ---< ---< ---|--------- SYN/ACK -----------< ---< ---< ---|
      |--- >--- >--- >---|----------- ACK ------------- >--- >--- >---|
      |               |                                         |
      |               | < --- connection deleted from table      |
      |               |                                         |
      |--- >- PSH - >---| < --- invalid connection                 |
      |               |                                         |

Linux下使用TCP keepalive

Linux 内置了对 keepalive 的支持。涉及 keepalive 的过程使用三个用户驱动的变量,可以使用 cat 查看参数值。

图片

前两个参数以秒表示,最后一个是纯数字。这意味着keepalive 例程在发送第一个keepalive 探测之前等待两个小时(7200 秒),然后每75 秒重新发送一次。如果连续9次没有收到 ACK 响应,则连接被标记为断开。

修改这个值很简单,可以这样修改:

echo 7000 > /proc/sys/net/ipv4/tcp_keepalive_time echo 40 > /proc/sys/net/ipv4/tcp_keepalive_intvl echo 10 > /proc/sys/net/ipv4/tcp_keepalive_probes

还有另一种访问内核变量的方法,使用 sysctl 命令

图片

setsockopt 、getsockopt 函数调用

在 Linux 操作系统中,我们可以通过代码启用一个 socket 的心跳检测,为特定套接字启用 keepalive 所需要做的就是在套接字本身上设置特定的套接字选项。函数原型如下:

int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);

int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

图片

第一个参数是socket;第二个必须是 SOL_SOCKET,第三个必须是 SO_KEEPALIVE。第四个参数必须是布尔整数值,表示我们要启用该选项,而最后一个是之前传递的值的大小。

在编写应用程序时,还可以为 keepalive 设置其他三个套接字选项。它们都使用 SOL_TCP 级别而不是 SOL_SOCKET,并且它们仅针对当前套接字覆盖系统范围的变量。如果不先写入就读取,将返回当前系统范围的参数。

TCP_KEEPCNT:覆盖 tcp_keepalive_probes
TCP_KEEPIDLE:覆盖 tcp_keepalive_time
TCP_KEEPINTVL:覆盖 tcp_keepalive_intvl

TCP keepalive 代码实现

在写TCP keepalive 服务程序时,除了要处理SIGPIPE外,还要有客户端连接检测机制,用于及时发现崩溃的客户端连接。我们使用TCP的 keepalive 机制方式。

tcp_keepalive_client:

int main(int argc, char *argv[])
{
 kat_arg0 = basename(argv[0]);
 bzero(&cp, sizeof (cp));
 cp.cp_keepalive = 1;
 cp.cp_keepidle = -1;
 cp.cp_keepcnt = -1;
 cp.cp_keepintvl = -1;

 while ((c = getopt(argc, argv, ":c:d:i:")) != -1) {
  switch (c) {
  case 'c':
   cp.cp_keepcnt = parse_positive_int_option(
       optopt, optarg);
   break;

  case 'd':
   cp.cp_keepidle = parse_positive_int_option(
       optopt, optarg);
   break;

  case 'i':
   cp.cp_keepintvl = parse_positive_int_option(
       optopt, optarg);
   break;

  case ':':
   warnx("option requires an argument: -%c", optopt);
   usage();
   break;

  case '?':
   warnx("unrecognized option: -%c", optopt);
   usage();
   break;
  }
 }

 if (optind > argc - 1) {
  warnx("missing required arguments");
  usage();
 }

 ipport = argv[optind++];
 if (parse_ip4port(ipport, &cp.cp_ip) == -1) {
  warnx("invalid IP/port: "%s"", ipport);
  usage();
 }

 (void) fprintf(stderr, "going connect to: %s port %dn",
     inet_ntoa(cp.cp_ip.sin_addr), ntohs(cp.cp_ip.sin_port));
 (void) fprintf(stderr, "set SO_KEEPALIVE  = %dn", cp.cp_keepalive);
 (void) fprintf(stderr, "set TCP_KEEPIDLE  = %dn", cp.cp_keepidle);
 (void) fprintf(stderr, "set TCP_KEEPCNT   = %dn", cp.cp_keepcnt);
 (void) fprintf(stderr, "set TCP_KEEPINTVL = %dn", cp.cp_keepintvl);
 rv = connectandwait(&cp);
 return (rv == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

tcp_keepalive_server:

int main(int argc, char *argv[] )
{

   /* 创建套接字 */
   if((listen_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
      perror("socket()");
      exit(EXIT_FAILURE);
   }

   /* 检查 keepalive 选项的状态  */
   if(getsockopt(listen_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) {
      perror("getsockopt()");
      close(listen_sock);
      exit(EXIT_FAILURE);
   }
   printf("SO_KEEPALIVE default is %sn", (optval ? "ON" : "OFF"));

   /* 将选项设置为活动  */
   optval = 1;
   optlen = sizeof(optval);

   if(setsockopt(listen_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
      perror("setsockopt()");
      close(listen_sock);
      exit(EXIT_FAILURE);
   }
   printf("SO_KEEPALIVE set on socketn");

   /* 再次检查状态  */
   if(getsockopt(listen_sock, IPPROTO_TCP, TCP_KEEPIDLE, &optval, &optlen) < 0) {
      perror("getsockopt()");
      close(listen_sock);
      exit(EXIT_FAILURE);
   }
   printf("TCP_KEEPIDLE is %dn", optval );
      /* 再次检查状态  */
   if(getsockopt(listen_sock, IPPROTO_TCP, TCP_KEEPCNT, &optval, &optlen) < 0) {
      perror("getsockopt()");
      close(listen_sock);
      exit(EXIT_FAILURE);
   }
   printf("TCP_KEEPCNT is %dn", optval);
      /* 再次检查状态  */
   if(getsockopt(listen_sock, IPPROTO_TCP, TCP_KEEPINTVL, &optval, &optlen) < 0) {
      perror("getsockopt()");
      close(listen_sock);
      exit(EXIT_FAILURE);
   }
   printf("TCP_KEEPINTVL is %dn", optval );
  /* 初始化套接字结构 */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   int portno = atoi(argv[1]);
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);

  ...
 }

图片

图片

程序创建一个 TCP 套接字并将 SO_KEEPALIVE 套接字选项设置为 1。如果指定了“-c”、“-d”和“-i”选项中的任何一个,则设置 TCP_KEEPCNT、TCP_KEEPIDLE 和 TCP_KEEPINTVL 套接字选项 在相应选项参数的套接字上。

通过测试程序,我们可以使用tcpdump、或者tshark是命令行抓包工具,来分析KeepAlive。

tshark -nn -i lo port 5050 tcpdump -nn -i lo port 5050

图片

tcpdump -nn -i lo port 5050

图片

整个keepalive过程很简单,就是client给server发送一个包,server返回给用户一个包。注意包内没有数据,只有ACK标识 被打开。

ps -aux | grep tcp_keepalive

图片

总结

keepalive 是一个设备向另一个设备发送的消息,用于检查两者之间的链路是否正在运行,或防止链路中断。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 电源
    +关注

    关注

    184

    文章

    17570

    浏览量

    249432
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1348

    浏览量

    78982
  • 程序
    +关注

    关注

    116

    文章

    3775

    浏览量

    80836
  • 网络通信
    +关注

    关注

    4

    文章

    786

    浏览量

    29755
收藏 人收藏

    评论

    相关推荐

    ESP32-S3使用tcp_server例程,将网络数据和串口数据透传延迟过高怎么解决?

    ;, errno);break;}tcp_sock = sock;// Set tcp keepalive optionsetsockopt(sock, SOL_SOCKET, SO_KEEP
    发表于 06-06 06:06

    ESP8266 TCP保持活动状态功能似乎不起作用,为什么?

    。 当我为TCP客户端设置保持活动状态参数时: ... client_fd= socket(); ... int keepAlive = 1; //enable keepalive int
    发表于 07-12 08:00

    如何使用espconn_set_keepalive

    我想建立一个 TCP 连接,该连接在第一次数据发送后不会关闭,我阅读了有关 espconn_set_保持活着 函数的信息,该函数将心跳发送到服务器,因此它不会断开连接(如果我理解正确的话)。但是我
    发表于 07-12 15:10

    使用esp_iot_sdk_v1.0.1_15_04_24时keepalive无效的原因?

    我的keepalive功能一直都正常,替换成新的esp_iot_sdk_v1.0.1_15_04_24 SDK后,keepalive就没有了,我的代码未变动 nKeepaliveParam
    发表于 07-12 09:28

    请问ESP8266 SDK可以添加KeepAlive获取状态接口吗?

    我在我的 TCP 套接字中启用了 keepalive,它可以发送和接收 keepalive packt 将本地服务器,但我突然关闭了我的本地服务器以测试 esp8266 在这种情况下可以注意到
    发表于 07-12 09:03

    求助,关于AT+CIPSTART指令keepalive功能的问题求解

    都没有任何响应,所以也不知道具体是多久断开。 所以尝试用AT+CIPSTART指令设置Keepalive功能来保持服务器连接,结果发现我设置成60秒,到了60秒就返回一个CLOSE。 一开始我以为是
    发表于 07-16 07:47

    LWIP的keepalive功能TCP长时间不再发送数据就会断开连接

    把LWIP中的keepalive功能打开之后,如果协议栈检测到TCP长时间不再发送数据就会把连接断开。如果我在网络正常通信的情况下突然拔掉网线,等到协议栈把TCP连接断开后,再插上网线,但是却不能重新连接之前的
    发表于 04-06 04:36

    tcp ip协议_什么是tcp ip协议

    什么是tcp ip协议,tcp ip协议详解,深刻讲述了tcp ip协议的概念,tcp ip协议层次等知识
    发表于 05-14 16:29 6004次阅读
    <b class='flag-5'>tcp</b> ip协议_什么是<b class='flag-5'>tcp</b> ip协议

    TCP实现:TCP输出

    学习TCP-IP的很好的书。TCP-IP详解卷3。
    发表于 05-09 14:33 0次下载

    TCP实现:TCP概要

    学习TCP-IP的很好的书。TCP-IP详解卷3。
    发表于 05-09 16:07 0次下载

    TCP实现:TCP用户请求

    学习TCP-IP的很好的书。TCP-IP详解卷3。
    发表于 05-09 16:07 0次下载

    TCP实现:TCP函数

    学习TCP-IP的很好的书。TCP-IP详解卷3。
    发表于 05-09 16:07 0次下载

    TCP实现:TCP输入

    学习TCP-IP的很好的书。TCP-IP详解卷3。
    发表于 05-09 16:07 0次下载

    CUBE配置STM32H750、Lan8720、FreeRTOS、lwip、掉线重连、KeepAlive移植

    重连实现2.2 TCP保活(keepalive)设定3. 代码实现3.1 Freertos.c3.2 tcpe
    发表于 12-27 18:48 91次下载
    CUBE配置STM32H750、Lan8720、FreeRTOS、lwip、掉线重连、<b class='flag-5'>KeepAlive</b>移植

    TCP keepalive机制具体是怎么样的

    今天,聊一个有趣的问题:拔掉网线几秒,再插回去,原本的 TCP 连接还存在吗?
    的头像 发表于 03-11 16:50 2782次阅读