文本主要是动手操作“小林coding”作者的丢包分析实验,感谢大神的总结,让我对TCP有了更深次的认识; 通过使用tcpdump、Wireshark抓包工具分析TCP丢包时过程;能跟踪、分析TCP三次握手具体发生了什么、不仅仅停留在理论与看blog上。
TCP第一次握手SYN包丢包
// 发送一个不存在地址的请求,用与模拟丢包情况
[root@~]$date; curl http://47.98.161.20 ;date
Sat Jun 6 15:06:06 CST 2020
curl: (7) Failed connect to 47.98.161.20:80; Connection timed out
Sat Jun 6 15:08:14 CST 2020
// 通过tcpdump抓包分析
[root@~]$tcpdump tcp and host 47.98.161.20 -w http-80-one.pcap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C7 packets captured
7 packets received by filter
0 packets dropped by kernel
使用Wireshark分析如下图
重试发生了6次;由以下系统参数决定
[root@~]$cat /proc/sys/net/ipv4/tcp_syn_retries
6
我们对该参数进行一次修改
echo 2 > /proc/sys/net/ipv4/tcp_syn_retries
重新进行一次抓包请求
[root@~]$date; curl http://47.98.161.20 ;date
Sat Jun 6 15:57:48 CST 2020
curl: (7) Failed connect to 47.98.161.20:80; Connection timed out
Sat Jun 6 15:57:55 CST 2020
发现本次消耗的时长变短了
通过Wireshark分析如下图
总结:
当客户端发起的TCP第一次握手syn包,在超时时间内没收到服务端的ack,就会在超时重传syn数据包,
每次超时重传的RTO是翻倍上涨的,直到syn包的重传次数到达tcp_syn_retries值后,客户端不再发送 syn包。
TCP第二次握手SYN包丢包
为了模拟客户端收不到服务端的响应,添加防火墙设置
// 添加防火墙
iptables -I INPUT -s 47.98.161.8 -j DROP
// 发送请求
[root@PHP-6-20 ~]# curl http://47.98.161.8:8080
curl: (7) Failed connect to 47.98.161.8:8080; 连接超时
// 抓包
[root@PHP-6-20 oyj]# tcpdump tcp and host 47.98.161.8 -w http-80-two.pcap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C6 packets captured
6 packets received by filter
0 packets dropped by kernel
通过Wireshark分析如下图
客户端角度:
客户端发起SYN后,由于防火墙屏蔽了服务端的所有数据包,所以curl是无法收到服务端的SYN、ACK包,
当发生超时后,就会重传SYN包;
服务端角度:
服务端收到客户的SYN包后,就会回SYN、ACK包,但是客户端一直没有回ACK,服务端在超时后,重传了
SYN、ACK 包,接着一会,客户端超时重传的SYN包又抵达了服务端,服务端收到后,超时定时器就重新
计时,然后回SYN、ACK包,所以相当于服务端的超时定时器只触发了一次,又被重置了;
客户端SYN超时重传次数达到了2次(上面重置过,一般tcp_syn_retries 默认值5次),就不再继
续发送SYN包了。
总结:
可以发现,当第二次握手的SYN、ACK丢包时,客户端会超时重发SYN包,服务端也会超时重传SYN、ACK包。
TCP第三次握手SYN包丢包
// 服务端添加防火墙,用于拒绝客户端的三次握手的ACK操作
iptables -I INPUT -s 192.168.12.37 -p tcp --tcp-flag ACK ACK -j DROP
// 具体操作分一下客户端和服务端
// 客户端
// 简历TCP连接
telnet 47.98.161.8:8080
[root@PHP-6-20 ~]# telnet 47.98.161.8 8080
Trying 47.98.161.8...
Connected to 47.98.161.8.
Escape character is '^]'.
// 抓包
[root@PHP-6-20 oyj]# tcpdump tcp and host 47.98.161.8 -w http-80-three.pcap
// 监控TCP状态
[root@PHP-6-20 ~]# netstat -napt | grep 47.98.161.8
tcp 0 0 192.168.6.20:38004 47.98.161.8:8080 ESTABLISHED 14424/telnet
[root@PHP-6-20 ~]#
// 服务端
[root@supervisor]$netstat -napt | grep 182.48.105.10
tcp 0 0 172.16.152.136:8080 182.48.105.10:32516 SYN_RECV -
总结
在建立 TCP 连接时,如果第三次握手的ACK,服务端无法收到,则服务端就会短暂处于SYN_RECV状态,
而客户端会处于 ESTABLISHED 状态。
由于服务端一直收不到TCP第三次握手的ACK,则会一直重传SYN、ACK包,直到重传次数超过tcp_synack_retries
值(默认值5次)后,服务端就会断开 TCP 连接。
TCP重传次数设置:
// 第一次握手重传次数限制
cat /proc/sys/net/ipv4/tcp_syn_retries
// 第二次握手重传次数限制
cat /proc/sys/net/ipv4/tcp_synack_retries
// 数据包最大重传次数限制
cat /proc/sys/net/ipv4/tcp_retries2
Linux控制keepalive有三个参数:
net.ipv4.tcp_keepalive_time=7200net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9保活时间net.ipv4.tcp_keepalive_time、保活时间间隔net.ipv4.tcp_keepalive_intvl、保活探测次数net.ipv4.tcp_keepalive_probes,是否复用time_wait net.ipv4.tcp_tw_reuse默认值分别是 7200 秒(2 小时)、75 秒和 9 次探测