TCP介绍

  • TCP 的 保活机制

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

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

一、三次握手

二、四次挥手

三、流量控制

四、拥塞控制

五、Wireshark 分析TCP

5.1、安装 Wireshark

5.2、Wireshark 可抓包数据信息

image.png

  • Frame:物理层的数据帧概况
  • Ethernet II: 数据链路层以太网帧头部信息
  • Internet Protocol Version 4: 互联网层IP包头部信息
  • Transmission Control Protocol:传输层T的数据段头部信息,此处是TCP
  • Hypertext Transfer Protocol: 应用层的信息,此处是HTTP协议

5.3、TCP包中的每个字段

TCP/IP 学习 - 图2

5.4、拦截查看请求过程

  • 拦截表达式

    1. # 指定请求主机名
    2. http.host == dev.ibuychem.com
  • 追踪 tcp 流

image.png

5.5、使用序列图可视化 tcp 握手过程

从菜单栏中,点击 统计 (Statistics) -> 流量图 (Flow Graph),然后,在弹出的界面中的「流量类型」选择 「TCP Flows」
image.png

  • 显示实际的序列号的值

取消相对值显示方式的勾选
image.png

六、实战

6.1、TCP 第一次握手的 SYN 丢包

6.1.1、模拟SYN丢包

模拟SYN丢包,就是客户端发送了 SYN 包,服务端没有收到,导致没有发回 ACK 包。所以,我们这里请求一个不存在的地址,进行模拟丢包行为。

  1. date;curl http://192.168.12.36;date

6.1.2、抓包

  1. # 表达式
  2. ip.addr == 192.168.12.36

image.png
当请求超时后,查看发现重传了六次,每次 RTO 超时时间是不同的:

  • 第一次是在 1 秒超时重传
  • 第二次是在 2 秒超时重传
  • 第三次是在 5 秒超时重传
  • 第四次是在 8 秒超时重传
  • 第五次是在 16 秒超时重传
  • 第六次是在 32 秒超时重传

可以发现,每次超时时间 RTO 是指数(翻倍)上涨的,当超过最大重传次数后,客户端不再发送 SYN 包。

6.1.3、查看在 Linux 中,第一次握手的 SYN 超时重传次数

  • 查看次数

    1. cat /proc/sys/net/ipv4/tcp_syn_retries
  • 修改

    1. echo 2 > /proc/sys/net/ipv4/tcp_syn_retries

6.2、TCP 第二次握手的 SYN、ACK 丢包

第二次握手的 SYN、ACK 丢包是客户端发送了 SYN 包,服务端在收到 客户端的 SYN 包后,响应客户端 SYN+ACK 包,但这时客户端收不到,导致服务的重发 SYN+ACK 包。 这里我们在客户端加上防火墙限制,直接粗暴的把来自服务端的数据都丢弃。

6.2.1、检查是否安装防火墙

  1. # 查看防火墙
  2. which iptables
  3. # 打印防火墙安装路径
  4. whereis iptables

6.2.2、客户端配置防火墙命令

  1. # 丢包
  2. # 192.168.120.24 服务端ip
  3. sudo iptables -I INPUT -s 192.168.120.24 -j DROP
  4. #sudo iptables -I INPUT -s 192.168.120.24 -j ACCEPT

6.2.3、访问

  1. date;curl http://192.168.120.24:9999/;date

6.2.4、抓包

  1. ip.addr == 192.168.120.24 and tcp.port == 9999

image.png
查看流量图
image.png
从图中可以发现:

  • 客户端发起 SYN 后,由于防火墙屏蔽了服务端的所有数据包,所以 curl 是无法收到服务端的 SYN、ACK 包,当发生超时后,就会重传 SYN 包
  • 服务端收到客户的 SYN 包后,就会回 SYN、ACK 包,但是客户端一直没有回 ACK,服务端在超时后,重传了 SYN、ACK 包,接着一会,客户端超时重传的 SYN 包又抵达了服务端,服务端收到后,超时定时器就重新计时,然后回了 SYN、ACK 包,所以相当于服务端的超时定时器只触发了一次,又被重置了。
  • 客户端 SYN 超时重传次数达到了 6 次,就不再继续发送 SYN 包了。
  • 注意:多次重传 SYN,会把服务端 SYN、ACK 超时定时器重置。

6.2.5、查看 SYN + ACK 重传次数

  1. cat /proc/sys/net/ipv4/tcp_synack_retries

客户端 SYN 包超时重传的最大次数,是由 tcp_syn_retries 决定的
服务端 SYN、ACK 包时重传的最大次数,是由 tcp_synack_retries 决定的

6.3、TCP 第三次握手的 ACK 包丢

为了模拟 TCP 第三次握手 ACK 包丢,我的实验方法是在服务端配置防火墙,屏蔽客户端 TCP 报文中标志位是 ACK 的包,也就是当服务端收到客户端的 TCP ACK 的报文时就会丢弃

6.3.1、配置服务端防火墙

  1. sudo iptables -I INPUT -s 192.168.138.117 -p tcp --tcp-flag ACK ACK -j DROP
  2. #sudo iptables -I INPUT -s 192.168.138.117 -p tcp --tcp-flag ACK ACK -j ACCEPT

6.3.2、访问

  1. # 服务端先启动服务
  2. # python3 -m http.server -d ./test/ 9999
  3. # 客户端访问 telnet 发起tcp 连接
  4. telnet 192.168.138.24 9999

6.3.3、查看tcp 连接状态

  1. # 查看客户端(在客户端执行,服务端ip 192.168.138.117)
  2. netstat -nat | grep 192.168.138.117
  3. tcp 0 1 192.168.138.117:47358 192.168.138.24:9999 FIN_WAIT1
  4. tcp 0 0 192.168.138.117:49364 192.168.138.24:9999 ESTABLISHED
  5. #查看服务端(在服务端执行,服务端ip 192.168.138.24)
  6. netstat -napt | grep 192.168.138.24
  7. tcp 0 0 192.168.138.24:9999 192.168.138.117:49026 SYN_RECV -
  8. #过一分钟后, 再执行上面的两个命令
  9. # 客户端依然还是处于 ESTABLISHED 状态
  10. netstat -nat | grep 192.168.138.117
  11. # 服务端原本处于 SYN_RECV 状态的连接消失了
  12. netstat -napt | grep 192.168.138.24
  1. hdj@hdj-PC:~$ telnet 192.168.138.24 9999
  2. Trying 192.168.138.24...
  3. Connected to 192.168.138.24.
  4. Escape character is '^]'.
  5. 1234 #输入 1234 字符,进行发送
  6. Connection closed by foreign host. # 过了好久才断开连接

6.3.4、抓包

  • 抓包规则

    1. ip.addr == 192.168.138.24 and tcp.port == 9999
  • 查看流量图

image.png
image.png
image.png

  • 客户端发送 SYN 包给服务端,服务端收到后,回了个 SYN、ACK 包给客户端,此时服务端的 TCP 连接处于 SYN_RECV 状态;
  • 客户端收到服务端的 SYN、ACK 包后,给服务端回了个 ACK 包,此时客户端的 TCP 连接处于 ESTABLISHED 状态;
  • 由于服务端配置了防火墙,屏蔽了客户端的 ACK 包,所以服务端一直处于 SYN_RECV 状态,没有进入 ESTABLISHED 状态
  • 接着,服务端超时重传了 SYN、ACK 包,重传了 5 次后,也就是超过 tcp_synack_retries 的值(默认值是 5),然后就没有继续重传了,此时服务端的 TCP 连接主动中止了,所以刚才处于 SYN_RECV 状态的 TCP 连接断开了,而客户端依然处于ESTABLISHED 状态;
  • 虽然服务端 TCP 断开了,但过了一段时间,发现客户端依然处于ESTABLISHED 状态,于是就在客户端的 telnet 会话输入了 1234 字符;
  • 此时由于服务端已经断开连接,客户端发送的数据报文,一直在超时重传,每一次重传,RTO 的值是指数增长的,所以持续了好长一段时间,客户端的 telnet 才报错退出了,此时共重传了 15 次。

6.3.5、数据包最大超时重传次数

  1. cat /proc/sys/net/ipv4/tcp_retries2
  • TCP 第一次握手的 SYN 包超时重传最大次数是由 tcp_syn_retries 指定
  • TCP 第二次握手的 SYN、ACK 包超时重传最大次数是由 tcp_synack_retries 指定
  • TCP 建立连接后的数据包传输,最大超时重传次数是由 tcp_retries2 指定

6.4、Wireshark可视化分析TCP流量控制

6.5、Wireshark可视化分析TCP 拥塞控制

参考