前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TCP keepalive特性解析

TCP keepalive特性解析

原创
作者头像
zenlu
修改2024-02-23 10:14:51
2300
修改2024-02-23 10:14:51
举报
文章被收录于专栏:学习笔记学习笔记

背景介绍

TCP keepalive机制最初是为了解决长时间处于空闲状态的连接问题而设计的。

在早期的TCP实现中,如果连接处于空闲状态,TCP协议不会发送任何数据包,这可能会导致网络中的路由器或防火墙关闭连接。

为了解决这个问题,TCP keepalive机制被引入到TCP协议中,它可以定期发送一些探测包来保持连接的活跃状态,从而避免连接被关闭。

当然,还有一种作用是:检测连接是否仍然处于活动状态。如果对端没有响应,就会认为服务端故障,可以及时关闭连接。

二 linux内核设置keepalive

操作系统设置tcp协议keep alive参数主要为以下三个文件:

代码语言:shell
复制
$ ll /proc/sys/net/ipv4/tcp_keepalive*
-rw-r--r-- 1 root root 0 May  6 16:59 /proc/sys/net/ipv4/tcp_keepalive_intvl
-rw-r--r-- 1 root root 0 May  9 16:31 /proc/sys/net/ipv4/tcp_keepalive_probes
-rw-r--r-- 1 root root 0 May  6 16:59 /proc/sys/net/ipv4/tcp_keepalive_time

三个文件默认的值分别为:

代码语言:shell
复制
$ cat /proc/sys/net/ipv4/tcp_keepalive*
75
9
7200

这三个文件的意义为:

  • tcp_keepalive_time(7200): 当连接已经两小时(7200秒)无活动时就发送一个探测包
  • tcp_keepalive_intvl(75):之后每75秒再次发送探测包
  • tcp_keepalive_probes(9):如果连续9次都没有收到ACK响应,则关闭连接

这些值无法通过vim进行修改,如果想要修改这些值,可以使用echo,例如:

echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time

会将7500改为600。

三 套接字的keepalive属性

上面的配置只是操作系统默认的TCP keepalive属性,实际上,TCP keepalive属性是可以通过套接字选项进行配置的。在实际进行通信时,我们需要查看具体的套接字属性,而不是仅仅依赖于操作系统的默认设置。

通过下面的程序,我们可以看到,在目前实验的机器上,默认的套接字keepalive属性是关闭的,并且套接字的属性是操作系统的TCP属性。当更改操作系统的TCP keepalive属性时,套接字的属性也会随之变动。

下面是一个示例程序,用于查看套接字的TCP keepalive属性:

代码语言:c
复制
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

int main(void);

int main() {
  int s;
  int optval;
  socklen_t optlen = sizeof(optval);

  /* Create the socket */
  if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
    perror("socket()");
    exit(EXIT_FAILURE);
  }

  /* Check the status for the keepalive option */
  if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &amp;optval, &amp;optlen) < 0) {
    perror("getsockopt()");
    close(s);
    exit(EXIT_FAILURE);
  }
  printf("default SO_KEEPALIVE setting is %s\n", (optval ? "ON" : "OFF"));

  /* Set the option active */
  optval = 1;
  optlen = sizeof(optval);
  if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &amp;optval, optlen) < 0) {
    perror("setsockopt()");
    close(s);
    exit(EXIT_FAILURE);
  }
  // printf("SO_KEEPALIVE set on socket\n");

  /* Check the status again */
  if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &amp;optval, &amp;optlen) < 0) {
    perror("getsockopt()");
    close(s);
    exit(EXIT_FAILURE);
  }
  printf("now SO_KEEPALIVE setting is %s\n", (optval ? "ON" : "OFF"));

  if (getsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, &amp;optval, &amp;optlen) < 0) {
    perror("setsockopt()");
    exit(EXIT_FAILURE);
  }
  printf("default TCP_KEEPCNT val %d\n", optval);

  if (getsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, &amp;optval, &amp;optlen) < 0) {
    perror("setsockopt()");
    exit(EXIT_FAILURE);
  }
  printf("default TCP_KEEPIDLE val %d\n", optval);

  if (getsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, &amp;optval, &amp;optlen) < 0) {
    perror("setsockopt()");
    exit(EXIT_FAILURE);
  }
  printf("default TCP_KEEPINTVL val %d\n", optval);

  close(s);

  exit(EXIT_SUCCESS);
}

通过 gcc -o test test.cc && ./test 执行,运行结果入下:

代码语言:shell
复制
default SO_KEEPALIVE setting is OFF
now SO_KEEPALIVE setting is ON
default TCP_KEEPCNT val 9
default TCP_KEEPIDLE val 7200
default TCP_KEEPINTVL val 75

如果按照第二章内容重设系统keep alive属性重新执行,会发现输出值也会相应变化。

四 本地实验

1. 第一次失败实验

下面我们编写一个TCP客户端和服务端,并使用抓包工具来查看TCP keepalive包的状态。我们将在客户端打开TCP keepalive,并设置探测包发送间隔为3秒,最大探测10次,探测间隔10秒。然后,在客户端与服务端建立连接之后,我们将关闭服务端,并使用抓包工具来查看TCP keepalive包的状态

抓包结果如下:

从图上可以看到,虽然我们关闭了server进程,但是client还是一直能收到8083端口的keep-alive ack回包,这是由于TCP keepalive ACK包是表明:服务端的操作系统内核仍然处于活动状态,而不是表明服务器仍然处于正常运行状态。如果服务器已经被杀掉或者出现了故障,TCP keepalive ACK回包仍然会被发送,换句话说,keepalive这个特性不会被用户态的我们感知到,这是完全作用在内核层面的东西

2. 第二次成功实验

这次我们希望模拟网络不通的情况,思路是,用自己的linux客户端程序访问我自己mac电脑上的服务端程序,建立连接后,关闭mac的WiFi,完整的抓包结果如下:

可以看到:

  • 客户端服务端先进行了三次握手后。
  • 由于连接不活动(我的程序只做了connect没有发送任何报文),3秒后,客户端开始向服务端发送keep alive探测包,服务端也有响应:
  • 9秒时,mac断网,隔了3秒钟,客户端又开始发送keep alive包,此时,服务端未回包(keep alive ack)
  • 10次后,客户端向服务端发送rst,关闭连接:

这个就和我设置的参数完全对应了。

六 总结

首先,我们介绍了TCP keepalive的背景和作用,包括保持连接的活跃状态、检测网络故障和服务器故障。然后,我们讨论了TCP keepalive的配置和调整,包括操作系统默认的TCP keepalive属性和套接字的TCP keepalive属性。最后,我们编写了一个TCP客户端和服务端,并使用抓包工具来查看TCP keepalive包的状态,以帮助我们更好地理解TCP keepalive机制的工作原理。

tip:

在某些场景下keep alive特性会非常重要,例如,ftp协议中,一次任务会建立两个tcp连接,一个用于传输,一个用于控制(比如控制上传哪个文件,中断传输等),在某些极端场景下,比如你要上传一个100G的文件,传输时间可能会好几个小时,这时,如果你用于控制的tcp连接被断掉了,那么可能你长久以来的努力都白费了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景介绍
  • 二 linux内核设置keepalive
  • 三 套接字的keepalive属性
  • 四 本地实验
    • 1. 第一次失败实验
      • 2. 第二次成功实验
      • 六 总结
        • tip:
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
        http://www.vxiaotou.com