TCP介绍
- TCP 的 保活机制
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
#表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
net.ipv4.tcp_keepalive_time=7200
#表示每次检测间隔 75 秒;
net.ipv4.tcp_keepalive_intvl=75
#表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。
net.ipv4.tcp_keepalive_probes=9
一、三次握手
二、四次挥手
三、流量控制
四、拥塞控制
五、Wireshark 分析TCP
5.1、安装 Wireshark
5.2、Wireshark 可抓包数据信息
- Frame:物理层的数据帧概况
- Ethernet II: 数据链路层以太网帧头部信息
- Internet Protocol Version 4: 互联网层IP包头部信息
- Transmission Control Protocol:传输层T的数据段头部信息,此处是TCP
- Hypertext Transfer Protocol: 应用层的信息,此处是HTTP协议
5.3、TCP包中的每个字段
5.4、拦截查看请求过程
拦截表达式
# 指定请求主机名
http.host == dev.ibuychem.com
追踪 tcp 流
5.5、使用序列图可视化 tcp 握手过程
从菜单栏中,点击 统计 (Statistics) -> 流量图 (Flow Graph),然后,在弹出的界面中的「流量类型」选择 「TCP Flows」
- 显示实际的序列号的值
取消相对值显示方式的勾选
六、实战
6.1、TCP 第一次握手的 SYN 丢包
6.1.1、模拟SYN丢包
模拟SYN丢包,就是客户端发送了 SYN 包,服务端没有收到,导致没有发回 ACK 包。所以,我们这里请求一个不存在的地址,进行模拟丢包行为。
date;curl http://192.168.12.36;date
6.1.2、抓包
# 表达式
ip.addr == 192.168.12.36
当请求超时后,查看发现重传了六次,每次 RTO 超时时间是不同的:
- 第一次是在 1 秒超时重传
- 第二次是在 2 秒超时重传
- 第三次是在 5 秒超时重传
- 第四次是在 8 秒超时重传
- 第五次是在 16 秒超时重传
- 第六次是在 32 秒超时重传
可以发现,每次超时时间 RTO 是指数(翻倍)上涨的,当超过最大重传次数后,客户端不再发送 SYN 包。
6.1.3、查看在 Linux 中,第一次握手的 SYN 超时重传次数
查看次数
cat /proc/sys/net/ipv4/tcp_syn_retries
修改
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、检查是否安装防火墙
# 查看防火墙
which iptables
# 打印防火墙安装路径
whereis iptables
6.2.2、客户端配置防火墙命令
# 丢包
# 192.168.120.24 服务端ip
sudo iptables -I INPUT -s 192.168.120.24 -j DROP
#sudo iptables -I INPUT -s 192.168.120.24 -j ACCEPT
6.2.3、访问
date;curl http://192.168.120.24:9999/;date
6.2.4、抓包
ip.addr == 192.168.120.24 and tcp.port == 9999
查看流量图
从图中可以发现:
- 客户端发起 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 重传次数
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、配置服务端防火墙
sudo iptables -I INPUT -s 192.168.138.117 -p tcp --tcp-flag ACK ACK -j DROP
#sudo iptables -I INPUT -s 192.168.138.117 -p tcp --tcp-flag ACK ACK -j ACCEPT
6.3.2、访问
# 服务端先启动服务
# python3 -m http.server -d ./test/ 9999
# 客户端访问 telnet 发起tcp 连接
telnet 192.168.138.24 9999
6.3.3、查看tcp 连接状态
# 查看客户端(在客户端执行,服务端ip 192.168.138.117)
netstat -nat | grep 192.168.138.117
tcp 0 1 192.168.138.117:47358 192.168.138.24:9999 FIN_WAIT1
tcp 0 0 192.168.138.117:49364 192.168.138.24:9999 ESTABLISHED
#查看服务端(在服务端执行,服务端ip 192.168.138.24)
netstat -napt | grep 192.168.138.24
tcp 0 0 192.168.138.24:9999 192.168.138.117:49026 SYN_RECV -
#过一分钟后, 再执行上面的两个命令
# 客户端依然还是处于 ESTABLISHED 状态
netstat -nat | grep 192.168.138.117
# 服务端原本处于 SYN_RECV 状态的连接消失了
netstat -napt | grep 192.168.138.24
hdj@hdj-PC:~$ telnet 192.168.138.24 9999
Trying 192.168.138.24...
Connected to 192.168.138.24.
Escape character is '^]'.
1234 #输入 1234 字符,进行发送
Connection closed by foreign host. # 过了好久才断开连接
6.3.4、抓包
抓包规则
ip.addr == 192.168.138.24 and tcp.port == 9999
查看流量图
- 客户端发送 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、数据包最大超时重传次数
cat /proc/sys/net/ipv4/tcp_retries2
- TCP 第一次握手的 SYN 包超时重传最大次数是由 tcp_syn_retries 指定
- TCP 第二次握手的 SYN、ACK 包超时重传最大次数是由 tcp_synack_retries 指定
- TCP 建立连接后的数据包传输,最大超时重传次数是由 tcp_retries2 指定