4个实验,彻底搞懂TCP连接的断开

前言

看到这个标题你可能会说,TCP 连接的建立与断开,这个我熟,不就是三次握手与四次挥手嘛。且慢,脑海中可以先尝试回答这几个问题:

  • 四次挥手是谁发起的?
  • 如果断电/断网了连接会断开吗?
  • 在什么情况下,如果没有四个人挥手,就会断开连接?
    [En]

    under what circumstances will the connection be disconnected without four waving?*

这不是一次采访,而是一个实际问题。至于它是什么,我不会在这篇文章中回答。稍后会有一篇专门的文章介绍问题是什么,所以在我们谈论实际问题之前,我们应该先了解理论。

[En]

This is not an interview, but a practical problem. As for what it is, I will not answer it in this article. Later, there will be a special article about what the problem is, so before we talk about the practical problem, we should understand the theory first.

正常断开

我们由浅入深,先了解正常情况下 TCP 连接是如何断开的,下图为 TCP 三次握手与四次挥手的经典图(来自《TCP/IP详解卷1》)

4个实验,彻底搞懂TCP连接的断开

在我们的电脑上,可以使用 python 的 SimpleHTTPServer 来快速起一个 http 服务(http 也是基于 TCP 协议),比如这样:

python -m SimpleHTTPServer 20880

再通过 nctelnet 这两个命令来创建 TCP 连接,比如我测试使用 nc 来创建连接

nc -v ip port

Connection to ip port [tcp/*] succeeded! 表示连接成功

4个实验,彻底搞懂TCP连接的断开

我们如何观察这个连接呢?可以通过 netstatlsof 来查看这条”连接”,这里我使用 lsof(mac 与 Linux 系统的 netstat 命令不太一样,使用起来有点别扭 )

lsof -i:20880

4个实验,彻底搞懂TCP连接的断开

客户端和服务器都占用一个端口,但服务器端口是固定的,客户端端口是随机的。

[En]

Both the client and the server occupy a port, but the server port is fixed and the client port is random.

如果我们想看 TCP 连接和断开时 握手挥手的 TCP 报文怎么查看呢?可以使用 tcpdump 命令

三次握手

tcpdump -A -vv -i any -S host 10.179.245.95

为了便于观看,它与上面的经典图片放在一起。

[En]

For easy viewing, it is put together with the classic picture above.

4个实验,彻底搞懂TCP连接的断开

这里的参数需要提一下的是 -S,如果不加 -S 参数看到的第三次握手的 ack=1,与书上的理论不太一样,其实这里只是 tcpdump 简化了展示,想看实际值需要加 -S

这里的 Flags [S]/[S.]/[.]

  • [S] 代表 SYN
  • [.] 代表 ACK,[S.] 就是 SYN + ACK

四次挥手

该命令与握手三次握手相同,我们捕获以下波动数据

[En]

The command is the same as the grasping three-way handshake, and we catch the following waving data

4个实验,彻底搞懂TCP连接的断开
  • [F] 代表 FIN

这张图有点奇怪,四次挥手居然变成了三次,这其实是 TCP 协议的实现问题,如果第二次与第三次挥手之间没有数据发送,那么被动断开连接的一方就可能会把第二次的 ACK 与 第三次的 FIN 合并为一次挥手。

当然,我已经捕捉到了四个正常的波,大概是这样的。

[En]

Of course, I’ve caught four normal waves, something like this.

4个实验,彻底搞懂TCP连接的断开

异常断开

有这么多床上用品,让我们开始谈正事吧。

[En]

With so much bedding on it, let’s get down to business.

TCP 连接断开是谁发起的

我们来思考一个问题:TCP 连接的断开是谁发起的?程序本身还是操作系统?

我们来看一段非常简单的 TCP 连接创建与断开的代码

tcpAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:20880")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
    fmt.Println("Client connect error ! " + err.Error())
    return
}

defer func() {
    err := conn.Close()
    fmt.Println("Client connect closed !")
    if err != nil {
        fmt.Println(err)
    }
}()

fmt.Println(conn.LocalAddr().String() + " : Client connected!")
time.Sleep(10 * time.Second)

运行后,效果如下,也符合我们预期:当程序打印 Client connected! 时,能看到连接,当打印 Client connect closed! 时,连接断开

4个实验,彻底搞懂TCP连接的断开

如果我们在连接断开前使用 kill -9 强杀进程呢?(这里我用了两台电脑来测试)

4个实验,彻底搞懂TCP连接的断开

我们发现 conn.Close() 并没有执行,但四次挥手还是发生了!

查阅资料发现如下结论:

a、b 两个正常连接的对端进程。假如 b 进程没有调用 close 就异常终止,那么发送 FIN 包是内核 OS 代劳

断电/断网时的连接是怎样断开的

通过以上实验,我们发现,即使进程异常终止,操作系统也会帮助发起四波。

[En]

Through the above experiments, we found that even if the process terminates abnormally, the operating system will help initiate four waves.

但如果是断电或断网的情况下,操作系统就无法代劳了,这时会怎样呢?为了便于测试,这里用两台电脑,client 连接 server,断开 server 的网络来模拟断网断电情况。

可以肯定的是 断网,断电后,连接不会立即断开,那么后续连接是否会断开呢?我们分成下面几种情况来看

断网时有数据传输

断网时如果有数据发送,由于收不到 ACK,所以会重试,但并不会无限重试下去,达到一定的重发次数之后,如果仍然没有任何确认应答返回,就会判断为网络或者对端主机发生了异常,强制关闭连接。此时的关闭是直接关闭,而没有挥手(数据都发不出去,还挥啥手),Linux 下的设置为

最小重传时间是200ms
最大重传时间是120s
重传次数为15

断电/断网时没有数据传输

断网时如果没有数据传输,还得看 TCP 连接的 KeepAlive 是否打开,关于 TCP 的 KeepAlive 简介如下:

  • TCP KeepAlive 是一种在不影响数据流内容的情况下探测对方的方式,采用 保活计时器实现,当计时器被触发时,一端发送保活报文,另一端接收到报文后发送 ACK 响应
  • 它并不是 TCP 的规范,但大部分的实现都提供了这一机制
  • 这一机制存在争议,一些人的生存机制应在申请中实施
    [En]

    this mechanism is controversial, and some people’s survival mechanism should be implemented in applications.*

开启KeepAlive

操作系统中有这么几个参数控制 KeepAlive 的配置:

  • Keepalive_time:空闲时间,即多长时间连接没有发送数据时开始 KeepAlive 检测
  • Keepalive_intvl:发送间隔时间,即上述代码的设置
  • Keepalive_probs:最多发送多少个检测数据包

在 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

4个实验,彻底搞懂TCP连接的断开

如果按照这个默认值来看,得2小时没有数据传输,KeepAlive 才开始工作!

而在 Go 中只有两个参数可以设置:

conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(5 * time.Second)

其中第二个 SetKeepAlivePeriod 源码是这样的:

func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
    // The kernel expects seconds so round to next highest second.
    secs := int(roundDurationUp(d, time.Second))
    if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err != nil {
        return wrapSyscallError("setsockopt", err)
    }
    err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)
    runtime.KeepAlive(fd)
    return wrapSyscallError("setsockopt", err)
}

SetKeepAlivePeriod 的参数同时设置了 tcp_keepalive_intvl 和 tcp_keepalive_time,tcp_keepalive_probes 没法设置

做个简单测试:client 开启 KeepAlive 连接 server 后,什么数据都不发送,把server 的网断掉,可以看到 KeepAlive 心跳包,一段时间后连接被置为 CLOSED 状态

4个实验,彻底搞懂TCP连接的断开

关闭KeepAlive

关闭 KeepAlive 后,如果没有数据传输,连接永远不会断开

断电/断网后 server 重启再恢复

再思考一个场景,如果 client 与 server 建立连接后,没有数据传输,断掉 server 端的网络,这时如果把 server 程序重启一下,再恢复网络,那这条连接还能用吗?

如果 server 重启后,client 还是不发数据,那这条连接看起来还是可用的,因为他们根本不知道对方是个什么情况,但如果此时 client 发送一点数据给 server,你会发现 server 会发送一个 RST 给client,然后 client 就断开连接了

4个实验,彻底搞懂TCP连接的断开

总结

除了正常情况之外,本文从 TCP 连接断开的角度结合实验给出了一些结论:

  • TCP 连接断开的 挥手,在进程崩溃时,会由操作系统内核代劳
  • 当 TCP 连接建立后,如果某一方断电或断网,如果此时刚好正在发送数据,TCP 数据包发送失败后会重试,重试达到上限时也会断开连接
  • 当 TCP 连接建立后,如果某一方断电或断网,且这条连接没有数据传输时
  • 如果开启了 KeepAlive 则会在一定心跳检测后断开连接,这个默认检测时间大概2个多小时,比较久
  • 如果未开启 KeepAlive 则连接永远存在
  • 如果一方发送 RST 包给另一方,也是会强制对方断开连接的

搜索关注微信公众号”捉虫大师”,后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。

4个实验,彻底搞懂TCP连接的断开

Original: https://www.cnblogs.com/zhuochongdashi/p/15465151.html
Author: 捉虫大师
Title: 4个实验,彻底搞懂TCP连接的断开

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/524419/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 博客怎么写才能更安全和简洁

    前言 博客实现本地存储 Markdown语法的介绍 博客对于我们普通人来说就是为了更好的去实现个人知识的一个整理融合然后把知识共享可以帮助其他去实现自己的一些工作或者学习中的一些疑…

    Linux 2023年6月14日
    089
  • Linux—OpenSSH服务

    Linux—OpenSSH服务 OpenSSH 是 SSH (Secure SHell) 协议的免费开源实现。SSH协议可以用来进行远程控制或在计算机之间传送文件。而实现此功能的传…

    Linux 2023年6月7日
    0104
  • 聊聊客户档案模型的设计与管理

    可以简单,更需要复杂; 一、基础描述 围绕客户管理通常分为售前、售中、售后、三个核心阶段,即营销、销售、服务三个核心流程与策略,在之前的文章中有聊过CDP系统的设计,本篇从客户档案…

    Linux 2023年6月14日
    083
  • 如何验收安卓PCBA主板的质量和性能

    .版本:v0.1作者:河东西望日期:2022-7-15. 对很多安卓智能设备厂商来说,他们的通用开发模式一般是:ODM/OEM设计开发主板PCBA(包括BSP驱动、原生AOSP系统…

    Linux 2023年6月7日
    0107
  • MS17-010复现

    一、环境准备 功击方:kali (192.168.43.132) 目标机:win7(192.168.43.134) win7打开smb服务 漏洞的产生: Sbm服务 445端口 二…

    Linux 2023年6月7日
    074
  • CH343芯片应用—Windows驱动安装与使用

    CH343属于沁恒第三代USB转串口芯片系列的单串口型号,基于经典版CH340芯片完成技术革新,实现USB转高速异步串口,支持最高6Mbps串口波特率。芯片支持使用厂商提供的VCP…

    Linux 2023年6月7日
    0104
  • burp通过条件竞争上传文件

    一·何为条件竞争现代框架更能抵御此类攻击。他们通常不会将文件直接上传到文件系统上的预期目的地。相反,他们采取了预防措施,例如首先上传到临时的沙盒目录并随机命名以避免覆盖现有文件。然…

    Linux 2023年6月7日
    089
  • 用go把博客园博客下载到本地Hexo目录下

    找到cookie 直接浏览器F12 巴拉巴拉 直接上代码 用hexo建静态博客的话,go文件在 \source\_posts目录下,run之后将会在此目录下生成cnblogs文件夹…

    Linux 2023年6月7日
    095
  • Vue3

    setup 函数时,它将接受两个参数:(props、context(包含attrs、slots、emit)) setup函数是处于 生命周期函数 beforeCreate 和 Cr…

    Linux 2023年6月13日
    086
  • ESXI系列问题整理以及记录——使用SSH为设备打VIB驱动包,同时提供一种对于ESXI不兼容螃蟹网卡(Realtek 瑞昱)的问题解决思路

    对于ESXI不兼容螃蟹网卡的问题,这里建议购买一张博通的低端单口千兆网卡,先使用博通网卡完成系统部署,再按照下文方法添加螃蟹网卡的VIB驱动,最后拆除博通网卡。 螃蟹网卡VIB驱动…

    Linux 2023年6月13日
    0104
  • cobbler

    cobbler cobbler cobbler简介 cobbler工作原理 cobbler的作用 cobbler服务端部署 cobbler简介 Cobbler是一个Linux服务器…

    Linux 2023年6月6日
    096
  • 【微服务】- 服务调用-OpenFeign

    服务调用 – OpenFeign 😄生命不息,写作不止🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆 一个有梦有戏的人 @怒放吧德德🌝分享学习心得…

    Linux 2023年6月6日
    097
  • Linux常用命令

    Linux常用命令 在学习瑞吉外卖项目课程中自己做的一个linux常用命令的小笔记便于以后复习使用,仅供参考。 文件目录操作命令 pwd:显示该目录或者文件的路径 ls命令 ls:…

    Linux 2023年6月7日
    086
  • Python闭包

    前言 学习Python的单例实现的时候,遇到了下面这样的代码。很不理解为什么局部变量 _instance没有重新初始化。后来看到有人说这是闭包,于是又去了解了下 闭包。没想到闭包竟…

    Linux 2023年6月7日
    098
  • 4.6 Linux解压.gz格式的文件(gunzip命令)

    gunzip 是一个使用广泛的解压缩命令,它用于解压被 gzip 压缩过的文件(扩展名为 .gz)。 对于解压被 gzip 压缩过的文件,还可以使用 gzip 自己,即 gzip …

    Linux 2023年6月7日
    072
  • 统计算法_概率基础

    本次有以下函数 1、简单边际概率 2、联合概率 3、条件概率 4、随机变量期望值 5、随机变量方差 6、随机变量协方差 7、联合协方差 8、组合期望回报 9、投资组合风险 说概率前…

    Linux 2023年6月6日
    086
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球