TCP连接状态详解

tcpdump 命令用法

  • 需要获取root权限才可以使用该指令

  • 先用ifconfig查看自己的网口有哪些
    我的主要是ens33

  • eg:tcpdump -i ens33 tcp and host 202.182.102.233 -nn

    -i ens33 表示抓取ens33网口的数据包

    icmp 表示抓取icmp协议的数据包

    host 表示主机过滤,抓取对应IP的数据包

    -nn 表示不解析IP地址和端口号的名称

    输出格式为

    时间戳 协议 源地址:源端口 > 目的地址.目的端口 网络包详细信息

  • -w ping.pcap 表示保存为名为ping.pcap的文件
    使用wireshark打开观察

    可以看出wireshark采用分层形式展现包的情况,从这个例子也可以看出网络各层都是有着自己负责的范围。
    上层协议完成相关工作就会交接给下一层,最终形成一个完整的数据包。

    1. ICMP协议:ICMP头 + Data
    2. IP层:IP头 | ICMP头 + Data
    3. 数据链路层:以太帧头 | IP头 | ICMP头 + Data

wireshark 用法

Ubuntu下需要获取root权限才能使用,用sudo wireshark打开软件。

TCP 三次握手和四次挥手

实验前提 测试的是我blog部署的主机202.182.102.233

在本地Ubuntu下设置两个终端分别执行下面命令:

  1. 客户端执行tcpdump抓包

    tcpdump -i any tcp and host 202.182.102.233 and port 80 -w http.pcap
  2. 客户端执行curl

    curl http://202.182.102.233

最开始三个包是TCP三次握手建立连接的包

中间是HTTP请求和响应的包

最后的四个包是TCP断开连接的挥手包

wireshark 点击Statistics(统计),选择Flow Graph(流量图),在下方的流量类型选择TCP Flows,得到下图。

这里的seq是相对值,不是绝对值。

右键菜单,选择Protocol Preferences,取消勾选Relative sequence numbers,再次重复上面步骤得下图。

实验

TCP第一次握手SYN丢包

先断开自己的VPN

一个终端使用tcpdump抓包

一个终端尝试与YouTube网站建立连接

抓包结果 SYN包重传了4次,其实应该重传6次,我的Ubuntu里内核超时重传参数值为6。

$ cat /proc/sys/net/ipv4/tcp_syn_retries
6

现在将其值设为2

$ echo 2 > /proc/sys/net/ipv4/tcp_syn_retries
2

重新做上面抓包步骤,得下图结果。

实验结论:当客户端发起连接,第一次握手SYN包在超时时间RTO内没收到服务端的ACK回应,就会再一次重传,每次超时重传的RTO是翻倍上涨的,直到传SYN包的次数到了系统的tcp_syn_retries值后,客户端就会停止发送请求连接SYN包。

TCP 第二次握手 SYN、ACK 丢包

实验失败,虚拟机问题,无语,太折腾,详情阅读学习指导

客户端 SYN 包超时重传的最大次数,是由 tcp_syn_retries决定的,默认值是 6 次;服务端 SYN、ACK 包时重传的最大次数,是由 tcp_synack_retries决定的,默认值是 5 次。--------这里的次数建议查看自己Linux系统的情况 ,貌似每个系统之间设置的参数数值不相同。

TCP 第三次握手 ACK 丢包

TCP 建立连接后的数据包传输,最大超时重传次数是由 tcp_retries2 指定,默认值是 15 次

$ cat /proc/sys/net/ipv4/tcp_retries2
15

TCP保活机制

这个机制定义一个时间段,这个时间段内,如果没有任何连接相关的活动,TCP保活机制就会开始起作用,每隔一个时间间隔,发送一个探测报文,这个报文所包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的TCP连接已经死亡,系统内核将错误信息通知给上层应用程序。

Linux内核有对象参数设置保活时间,保活探测的次数,保活探测的时间间隔

net.ipv4.tcp_keepalive_time=7200
# 表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
net.ipv4.tcp_keepalive_intvl=75
# 表示每次检测间隔 75 秒
net.ipv4.tcp_keepalive_probes=9
# 表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。

即在Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。

tcp_keepalive_time + (tcp_keepalive_intvl * tcp_keepalive_probes)

7200 + (75 * 9) = 7895 秒 (2小时11分15秒)

抓包时间足够长,说不定能抓到探测报文

在建立 TCP 连接时,如果第三次握手的 ACK,服务端无法收到,则服务端就会短暂处于 SYN_RECV 状态,而客户端会处于 ESTABLISHED 状态。

由于服务端一直收不到 TCP 第三次握手的 ACK,则会一直重传 SYN、ACK 包,直到重传次数超过 tcp_synack_retries 值(默认值 5 次)后,服务端就会断开 TCP 连接。

而客户端则会有两种情况:

  • 如果客户端没发送数据包,一直处于 ESTABLISHED 状态,然后经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接,于是客户端连接就会断开连接。
  • 如果客户端发送了数据包,一直没有收到服务端对该数据包的确认报文,则会一直重传该数据包,直到重传次数超过 tcp_retries2 值(默认值 15 次)后,客户端就会断开 TCP 连接。

TCP快速建立连接

Linux下修改net.ipv4.tcp_fastopen内核参数开启Fast open功能

让服务端能够在第一次与客户端连接后,产生一个Cookie值通过SYN,ACK包一起发送给客户端,客户端缓存这个Cookie,在第二次请求时,能够这个值随SYN请求包一同发给服务端,服务端就可以跳过三次握手的过程,这个整个时延为一个RTT

$ sysctl -w net.ipv4.tcp_fastopen=3
  • 0 关闭
  • 1 作为客户端使用 Fast Open 功能
  • 2 作为服务端使用 Fast Open 功能
  • 3 无论作为客户端还是服务器,都可以使用 Fast Open 功能
Last modification:October 6, 2023
兴趣使然