0 前言

本文将主要通过抓包并查看报文的方式学习TCP KeepAlive机制,以此加深理解。

1 TCP KeepAlive机制简介

TCP长连接下,客户端和服务器若长时间无数据交互情况下,若一方出现异常情况关闭连接,抑或是连接中间路由出于某种机制断开连接,而此时另一方不知道对方状态而一直维护连接,浪费系统资源的同时,也会引起下次数据交互时出错。

为了解决此问题,引入了TCP KeepAlive机制(并非标准规范,但操作系统一旦实现,默认情况下须为关闭,可以被上层应用开启和关闭)。其基本原理是在此机制开启时,当长连接无数据交互一定时间间隔时,连接的一方会向对方发送保活探测包,如连接仍正常,对方将对此确认回应。

关于TCP KeepAlive机制的详细背景可以参考《TCP/IP详解 卷1:协议》一书,在此不详细赘述。

2 TCP KeepAlive设置参数和报文格式简介

2.1 TCP KeepAlive参数

TCP KeepAlive机制主要涉及3个参数:

  • tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
    The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep-alives are sent only when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2 hours). An idle connection is terminated after approximately an additional 11 minutes (9 probes an interval of 75 seconds apart) when keep-alive is enabled.
    在TCP保活打开的情况下,最后一次数据交换到TCP发送第一个保活探测包的间隔,即允许的持续空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2h)。

  • tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
    The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no response is obtained from the other end.
    在tcp_keepalive_time之后,最大允许发送保活探测包的次数,到达此次数后直接放弃尝试,并关闭连接,默认值为9(次)。

  • tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)
    The number of seconds between TCP keep-alive probes.
    在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包的发送频率,默认值为75s。

2.2 TCP KeepAlive报文格式

TCP KeepAlive探测报文是一种没有任何数据,同时ACK标志被置上的报文,报文中的序列号为上次发生数据交互时TCP报文序列号减1。比如上次本端和对端数据交互的最后时刻,对端回应给本端的ACK报文序列号为 N(即下次本端向对端发送数据,序列号应该为N),则本端向对端发送的保活探测报文序列号应该为 N-1。 在本文第四节,将通过抓包的方式再次介绍TCP KeepAlive报文。

3 TCP KeepAlive的配置

3.1 系统内核参数配置

Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。

  • 查看当前内核TCP KeepAlive参数

    sysctl net.ipv4.tcp_keepalive_time
    sysctl net.ipv4.tcp_keepalive_probes
    sysctl net.ipv4.tcp_keepalive_intvl
  • 修改TCP KeepAlive参数
    sysctl net.ipv4.tcp_keepalive_time=3600

3.2 C语言socket设置

对于Socket而言,可以在程序中通过socket选项开启TCP KeepAlive功能,并配置参数。对应的Socket选项分别为SO_KEEPALIVETCP_KEEPIDLETCP_KEEPCNTTCP_KEEPINTVL

int keepalive = 1;          // 开启TCP KeepAlive功能
int keepidle = 7200; // tcp_keepalive_time
int keepcnt = 9; // tcp_keepalive_probes
int keepintvl = 75; // tcp_keepalive_intvl setsockopt(socketfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
setsockopt(socketfd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof (keepidle));
setsockopt(socketfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof (keepcnt));
setsockopt(socketfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof (keepintvl));

4 抓包实践分析

4.1 实践环境设置

将利用C语言分别编写服务器侧的代码和客户端的代码,在服务器侧开启TCP KeepAlive功能,服务器的端口号设置为6699,客户端的端口号为内核自动分配,并且每次客户端重启后内核分配的端口号可能不同,因此只需记住服务器端口号即可,另外一个相对的就是客户端。

服务器侧TCP KeepAlive参数具体设置如下:

tcp_keepalive_time = 55s
tcp_keepalive_probes = 2
tcp_keepalive_intvl = 6s

客户端运行于个人MAC电脑上,服务器运行在另外一台Linux系统上,后续通过wireshark软件进行抓包也是在MAC电脑上进行抓包。

4.2 wireshark抓包分析

将服务器开启,并通过客户端与之建立连接,过程中客户端和服务器无任何数据交互。通过wireshark抓包分析客户端和服务器间的数据交互,如下图:

可以看到,客户端和服务器建立连接后,服务器每隔55s发送了一次TCP KEEPALIVE的探测报文,也验证了上面服务器tcp_keepalive_time的设置,客户端收到探活报文后,会作出回应。点击具体报文可以查看报文详情,如下图所示:

本文2.2节中介绍过TCP KEEPALIVE探活报文的序列号为上次发生数据交互时TCP报文序列号减1,实践中客户端和服务器在建立连接三次握手之后,没有发生任何数据交换,因此服务器收到客户端发送的最后报文的ACK值为1(注意wireshark抓包的报文序列号为相对序列号,Relative Sequence Number),所以服务器发送保活探测报文的序列号应该为0,客户端收到服务器探活报文后回应的确认报文序列号为1,和wireshark实际抓包相符合。

4.3 进一步测试

上面的测试中验证了tcp_keepalive_time参数,接下来验证TCP KeepAlive机制中另外两个参数。为了模拟由于连接中间路由等异常导致探活报文收不到回应的场景,借助Linux iptables命令设置防火墙,阻断TCP探活报文的传输。

这里将来自客户端的报文丢弃,即模拟服务器发送TCP KEEPALIVE探活报文后迟迟收不到回应报文的场景。使用的命令为:sudo iptables -A INPUT -p tcp --sport $YOUR_CLIENT_PORT -j DROP,其中sport为系统为客户端分配的端口号。

注意服务器是运行在Linux系统上的,客户端和wireshark运行在本地MAC电脑上,因此利用上述iptables设置防火墙规则后,wireshark仍能抓到所有确认报文,只是在Linux系统这一侧通过软件防火墙将报文丢弃了。

设置报文过滤规则后,再次通过wireshark抓包,如下图:

从捕捉到的报文可以看到,3024.35s时间戳开始,服务器发送TCP KEEPALIVE探活报文后,一直收不到确认报文的返回,便隔6秒重新发送一个探活报文,即上文提到的服务器tcp_keepalive_intvl参数的设置。再等待2个tcp_keepalive_intvl时间间隔后,服务器仍未收到确认报文后,服务器发送了RST报文,以释放网络连接资源,这里的2次即tcp_keepalive_probes设置的。

注意:在完成上述测试后,借助sudo iptables -F命令清除所有的防火墙规则。

4 总结

本文借助wireshark软件,对TCP KEEPALIVE报文进行抓包分析,分析了TCP保活机制中tcp_keepalive_timetcp_keepalive_probestcp_keepalive_intvl三个参数的实际效果,加深了对TCP KEEPALIVE机制的理解。

参考资料

[1] About TCP keepalive: (http://baotiao.github.io/tech/2015/09/25/tcp-keepalive/)
[2] https://www.codenong.com/cs105424711/

TCP KeepAlive机制理解与实践小结的更多相关文章

  1. 心跳机制tcp keepalive的讨论、应用及“断网”、"断电"检测的C代码实现(Windows环境下)

    版权声明:本文为博主原创文章,转载时请务必注明本文地址, 禁止用于任何商业用途, 否则会用法律维权. https://blog.csdn.net/stpeace/article/details/441 ...

  2. 聊聊 TCP 中的 KeepAlive 机制

    KeepAlive并不是TCP协议规范的一部分,但在几乎所有的TCP/IP协议栈(不管是Linux还是Windows)中,都实现了KeepAlive功能 RFC1122#TCP Keep-Alives ...

  3. 聊聊TCP Keepalive、Netty和Docker

    聊聊TCP Keepalive.Netty和Docker 本文主要阐述TCP Keepalive和对应的内核参数,及其在Netty,Docker中的实现.简单总结了工作中遇到的问题,与大家共勉. 起因 ...

  4. tcp keepalive选项

    之前一直对tcp keepalive选项理解有误, 以为通过setsockopt函数设置SO_KEEPALIVE和相关参数后该socket则使用设置的keepalive相关参数 否则使用系统默认的:k ...

  5. TCP Keepalive笔记

    TCP是无感知的虚拟连接,中间断开两端不会立刻得到通知.一般在使用长连接的环境下,需要心跳保活机制可以勉强感知其存活.业务层面有心跳机制,TCP协议也提供了心跳保活机制. 长连接的环境下,人们一般使用 ...

  6. HTTTP及TCP的超时以及KEEP-ALIVE机制小结

    一.HTTP的超时和Keep Alive HTTP Keepalive 机制是http 1.1中增加的一个功能. 在HTTP 1.0中,客户端每发起一个http 请求,等收到接收方的应答之后就断开TC ...

  7. 闲说HeartBeat心跳包和TCP协议的KeepAlive机制

    很多应用层协议都有HeartBeat机制,通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据.使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议. ...

  8. TCP编程实践小结1

    说起TCP/IP协议,大家估计都能说出个一二,但是估计很少有人能够深入的理解这个协议,原因有这么几个: 协议本身确实复杂 入门教材没选对,太抽象了,导致大家浅尝辄止 学习过程中如果没有配合实践理解,过 ...

  9. 在Linux环境下使用TCP的keepalive机制

    Linux内置支持keepalive机制,为了使用它,你须要使能TCP/IP网络,为了可以配置内核在执行时的參数.你还须要procfs和sysctl的支持. 这个过程涉及到keepalive使用的三个 ...

随机推荐

  1. Android数据存取

    Android数据存取 一.SharedPreferencesc存取数据 SharedPreferences是使用键值对的方式来存储数据的,也就是在保存一条数据时,需要给这条数据提供一个对应的键,这样 ...

  2. Tomcat中的Server.xml配置详解

    Tomcat中的Server.xml配置详解 Tomcat Server的结构图如下: 该文件描述了如何启动Tomcat Server <Server> <Listener /> ...

  3. redis入门到精通系列(七):redis高级数据类型详解(BitMaps,HyperLogLog,GEO)

    高级数据类型和五种基本数据类型不同,并非新的数据结构.高级数据类型往往是用来解决一些业务场景. (一)BitMaps (1.1) BitMaps概述 在应用场景中,有一些数据只有两个属性,比如是否是学 ...

  4. Linux上Zookeeper集群搭建

    一.官网 https://zookeeper.apache.org/ 二.下载安装 (1)下载 复制链接地址  http://mirror.bit.edu.cn/apache/zookeeper/zo ...

  5. Spring 的 init-method 和 destory-method

    关于在spring  容器初始化 bean 和销毁前所做的操作定义方式有三种 第一种注解: 通过@PostConstruct 和 @PreDestroy 方法 实现初始化和销毁bean之前进行的操作 ...

  6. JpaRepository 增删改查

    Jpa查询 JpaRepository简单查询 基本查询也分为两种,一种是spring data默认已经实现,一种是根据查询的方法来自动解析成SQL. 预先生成方法 spring data jpa 默 ...

  7. java中关于string类和常量池的一点猜想

    public class StringTest { /**   * @param args   */  public static void main(String[] args) {   test1 ...

  8. 20 个 .NET 6 新增的 API

    DateOnly & TimeOnly .NET 6 引入了两种期待已久的类型 - DateOnly 和 TimeOnly, 它们分别代表DateTime的日期和时间部分. DateOnly ...

  9. centos源码部署lua-5.3

    目录 一.介绍 二.部署 三.测试 一.介绍 Luat语言是在1993年由巴西一个大学研究小组发明,其设计目标是作为嵌入式程序移植到其他应用程序,它是由C语言实现的,虽然简单小巧但是功能强大. 二.部 ...

  10. 【dva】如何监听异步请求是否完成(页面loading)

    方案1.你可以在model里面操作 在model里面的state里面声明一个变量state,默认是false,effect函数执行开始就将其改为true,然后等call()然后结束后又将其改为fals ...