赞
踩
在使用 wireshark 抓包观察 TCP 连接的时候,我发现在我与服务器建立 TCP 连接,并完成 HTTP 请求获取到数据后,服务器与我并不会马上进行 四次挥手 断开连接,而是会进行一段时间的 Keep-Alive。
这就很有意思了,Keep-Alive 这段时间是如何控制的呢?其机制是怎样的?这就是本篇想聊清楚的问题。
嗯…… 为了鲜明标识,决定 【本文将带你学会】改名为 【搁浅一下】 意思不变
下图是我一次接口请求的 wireshark 抓包,我们看图说话
开始的是 三次握手 忘记的同学可以看我之前的文章,这里重点看 Keep-Alive
好,此时我们什么都不知道,但是可以根据抓包先观察分析猜测一下。
分析上图流程
推测
ok 这是我的一个分析推测,那么实际是如何呢?
首先明确一点,TCP Keep-Alive 并不是 TCP 标准的一部分,而是由协议栈实现者进行拓展实现,而我们主流的系统 Linux、Windows、MacOS、BSD 都进行了对应的实现。
在 RFC1122 (Request For Comments) 给出了标准,以及为何 TCP 标准并不包含 keep-alive 机制的原因 RFC1122 Page-101
该标准定义了以下几点:
在 DISSCUSSION 中 RFC1122 给出了这样的建议
为了确认空闲连接仍然活着,这些实现会发送探测段,旨在引发对等 TCP 响应,这样的探测端通常会包含 SEG.SEQ = SND.NXT - 1
并可能包含 8 字节无效数据。即 keep-alive 包 seq 值等于前一个报文的 seq - 1
如我图中所示,第一个 keep-alive 报文的 seq = 293 是上一个 (seq = 294) -1。
因此这个探测包会导致接收者回复一个 Ack 报文段,以此来确认此连接仍然存活,如果对方因为网络分区或者崩溃而断开连接,它将以 RST 响应,而不是 ACK。
SEG.SEQ = SND.NXT - 1
即 keep-alive 包 seq 值等于前一个报文的 seq - 1 从而引发对端的 ACK 报文。
控制 TCP Keep-Alive 的核心参数大概有三个,你可以通过以下命令查看
Linux 系统
cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes
上面三行命令你应该可以在 Linux 系统下查到对应的配置
net.ipv4.tcp_keepalive_time=7200 // 发送的最后一个数据包(简单的 ACK 不被视为数据)和第一个 keepalive 探测包之间的间隔,单位 秒,如默认是 7200 秒,则为 7200 秒后发送第一个 keeplive 探测包
net.ipv4.tcp_keepalive_intvl=75 // 后续发送 keepalive 探测包的时间间隔,无论连接在此期间交换了什么,比如默认是 75s 的间隔发送下一个探测
net.ipv4.tcp_keepalive_probes=9 // 在确认连接死亡前,要发送的未被确认即没有被 keepalive-ack 的探测包数量
关于配置,大家可以参考这篇文章 usingkeepalive
Mac OS 系统
查看命令
sysctl net.inet.tcp | grep -E "keepidle|keepintvl|keepcnt"
net.inet.tcp.keepidle: 7200000 // 同 linux tcp_keepalive_time 单位 毫秒
net.inet.tcp.keepintvl: 75000 // 同 linux tcp_keepalive_intvl 单位 毫秒
net.inet.tcp.keepcnt: 8 // 同 linux tcp_keepalive_probes
那这里就有些奇怪,我的系统是 Mac OS ,配置如上图,然而我的 keepalive 探测包在大概 1s 后就开始发送了,与系统配置不符
这里又牵扯出另一个概念 HTTP keep-alive
我的 HTTP 协议版本是 1.1 ,他默认是开启 keep-alive 的,这里我通过设置他的 Header Connection 为 Close 关闭 keep-alive
wireshark 抓包如上,我进行了两次请求,此时没有了 keep-alive 包,而是在每次请求数据交换完毕后就直接进行了挥手断连。
而我如果开启 Connection = keep-alive 进行两次请求呢?
这里很明显的进行了 keep-alive ,并且两次请求复用了一个 TCP 连接,整个过程仅有一次 握手建立连接 和 挥手断开连接。
所以普遍的,我们内核系统的 TCP Keep-Alive 是默认关闭的,你也可以通过程序代码配置当前 TCP Socke 开启保活机制,而 HTTP 的 Keep-Alive 可以影响 TCP 层面的 Keep-Alive 也就是我们是否复用一个 TCP 连接进行 HTTP 请求
吐槽时刻:
不行了,我得吐槽下,这部分可略过哈。
我原以为,这篇文章应该比较短,然而越看牵扯东西越多,想讲清楚不容易啊,而且最后由解释 Keep-Alive 机制 变成了:
到底是谁控制了我的 TCP Keep-Alive 间隔,至少到目前,系统层面的配置是没生效的,毕竟 2 小时开始发,结果我这 1 s 后就开始发了。
临时起意,加一部分,TCP Keep-Alive 、HTTP Keep-Alive、Nginx Keep-Alive、Tomcat Keep-Alive、Undertow Keep-Alive、客户端、服务端,到底是谁的配置在生效?!!(说实话,这块都能独立写一篇了,要不要拆分呢?)
时隔一周 摸了一周,换换脑子
以上,我怀疑是我请求的测试服务器配置的问题,这里我尝试请求 CSDN 北京服务器的 ip 182.92.187.217
结果是喜人的,干干净净的抓包
看的出来,从握手,到 HTTP ,到确认收到后,我们直接进行了挥手断连,我请求的 keep-alive 参数完全没有起作用(这说明服务器并没有做相关配置)啊……干净的让人舒服,教课级别的请求断连流程,让我们为 CSDN 鼓掌,啊。
这时候可能就有人要问了:”啊(尖声)!那你,前面的 keep-alive 包是怎么回事?是谁配置的?谁的配置在生效?“
好!好问题,谁问的? 这位同学请出门右转好吧 (@**@¥#%¥&*&)
留个悬念,我们下期再见!(一定不是不想写了才这样结束的)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。