tcp三次握手丢包后会发生什么

测试工具

本片文章会用到以下工具来学习tcp三次握手:

  1. tcpdump,一个运行在用户态的应用程序,它本质上是通过调用 libpcap 库的各种 api 来实现数据包的抓取功能。数据包到达网卡后,经过数据包过滤器(BPF)筛选后,拷贝至用户态的 tcpdump 程序,以供 tcpdump 工具进行后续的处理工作,输出或保存到 pcap 文件。我们用tcpdump来抓取三次握手的报文
  2. iptables,也是一个运行在用户态的应用程序,底层用的内核模块netfilter,是一个linux防火墙,我们用它来模拟三次握手的丢包。
  3. curl,用来作为测试的客户端。
  4. nginx,用来作为测试的服务端。
    接下来我们就进入正题–

三次握手丢包后会发生什么?

接下来给大家来演示一下tcp三次握手如果出现丢包会发生什么。
我们使用的客户端ip:xxx.xxx.xxx.92
我们使用的服务端ip:xxx.xxx.xxx.95

正常的三次握手

我们首先来抓包看一下正常的三次握手。下图是一个tcp通信的全过程。
在这里插入图片描述

客户端

在客户端使用curl来发送一个http请求,并用tcpdump来进行抓包,过滤服务端ip。

tcpdump -i any host xxx.xxx.xxx.95 -n

在这里插入图片描述
抓包结果如上图所示,前三个包是我们熟知的三次握手(’.'是ACK),后面四个包是http数据传输,最后的三个包是tcp四次挥手。四次挥手为啥只有三个包,因为服务端收到客户端发送过来的fin包之后,没有数据需要传输,所以将第二次、第三次挥手合在一块了。

服务端

服务端抓包过滤客户端ip:

tcpdump -i any host xxx.xxx.xxx.92 -n

由于没有丢包,抓包结果和客户端完全一致。

第一次握手丢包

我们首先进行理论分析。如果第一次握手的syn包丢了,那首先服务端不会有任何反应,客户端由于收不到第二次握手的synack包,所以会进行重传,重传一定次数 (由net.ipv4.tcp_syn_retries内核参数控制)之后,客户端会自动销毁这个连接。
我们用iptables来模拟丢包,值得一提的是,tcpdump和iptables有一定的先后顺序。入方向的话,tcpdump运行在iptables的前面,出方向是相反的。我们可以非常简单的把我们的测试环境拓扑看成 客户端应用程序(curl)->客户端的iptables->客户端的tcpdump->服务端的tcpdump->服务端的iptables->服务的的应用程序(nginx),所以如果在入方向用iptables丢包,tcpdump也可以抓到。如果你在实践过程中发现用iptables丢的包,tcpdump依然可以抓到,那就可以想一下这个拓扑。
用iptables来丢弃92发送过来的syn包(两个命令都是一样的效果,区别是用tcpdump抓包看到的结果不同,原因上面说了):

client:
# iptables -A OUTPUT -p tcp -d xxx.xxx.xxx.95 --tcp-flags ALL SYN -j DROP

server:
# iptables -A INPUT -p tcp -s xxx.xxx.xxx.92 --tcp-flags ALL SYN -j DROP

客户端

在这里插入图片描述
可以看到客户端重传三次之后释放了连接,第一次重传时间是1s,之后每次重传时间是之前的2倍。

服务端

服务端抓不到包,和我们的预期相符。

第二次握手丢包

第二次握手丢包也比较简单,客户端由于收不到第二次握手的synack包,会进行重传,而服务端收不到第三次握手的ack包,也会进行重传(由net.ipv4.tcp_synack_retries内核参数控制)。
我们同样用iptalbes来模拟丢包。

client:
# iptables -A INPUT -p tcp -s xxx.xxx.xxx.95 --tcp-flags ALL SYN,ACK -j DROP

server:
# iptables -A OUTPUT -p tcp -d xxx.xxx.xxx.92 --tcp-flags ALL SYN,ACK -j DROP

客户端

在这里插入图片描述
可以看到客户端现象和之前第一次握手丢包一样,重传失败三次之后释放了连接。

服务端

在这里插入图片描述
服务端由于在重传第二次握手的过程中,又收到了客户端重传过来的syn包,所以看起来比较乱一点。我们简单数一下,服务端一共收到了4个syn包(同序列号),发出去了6个synack包(同序列号),相当于重传了两次,与内核参数net.ipv4.tcp_synack_retries = 2一致。
测试中还遇到一个场景,服务端在重传的过程中有概率换一个序列号,如果换了一个序列号,那这个序列号的第二次握手也要重传两次。

第三次握手丢包

第三次握手情况比前两种复杂一些,我们还是先从理论上分析一下。假如第三次握手客户端发送的ack包丢了,客户端是不知道的,因为只要第三次握手发出之后,客户端就认为连接已经建立了,状态变成ESTABLISHED;而服务端由于收不到第三次握手的ack包,状态依然为SYN_REVD,理论上服务端会进行重传,重传一定次数之后会释放连接。而客户端如果发送数据,服务端收到请求之后,由于不处于ESTABLISHED状态,可能认为这不是一个正常的连接,然后回个rst;如果客户端不发送数据,那可能就会hang住,只能等待keepalive超时。事实真的是这样吗,我们来实践一下。

丢包后客户端发送数据

我们依然用iptables来模拟丢包

client:
# iptables -A OUTPUT -p tcp -d xxx.xxx.xxx.95 --tcp-flags ALL ACK -j DROP

server:
# iptables -A INPUT -p tcp -s xxx.xxx.xxx.xxx.92 --tcp-flags ALL ACK -j DROP
客户端

在这里插入图片描述

服务端

在这里插入图片描述
root@xxx-xxx-xxx-92:~# curl xxx.xxx.xxx.95:8004
{“host”: “xxx.xxx.xxx.95”, “port”: “80”}
我们先看结论,可以看到这个请求竟然通了,但是在服务端抓包看到只抓到了前两次握手,第三握手的ack包确实丢了。上网查了下资料发现原因就是服务端收到的第三个包:psh+ack包。就像上面我们分析的,客户端发送完第三次握手之后,就认为连接已经建立,随后就开始发送数据了,这个间隔是很小的(可以看到只有0.00001s);而服务端的第二次握手重传需要等待1s,所以还没来得及重传,服务端就已经收到了客户端发送过来的第一个数据包。又因为客户端发送过来的第一个数据包里面带着ack的flag,所以服务端会正常进入ESTABLISHED状态。

丢包后客户端不发送数据

如果客户端不发送数据会出现什么现象呢,我们用iptables来模拟丢弃第三次握手和客户端发送的第一个数据包。

client:
# iptables -A OUTPUT -p tcp -d xxxx.95 --tcp-flags ALL ACK -j DROP
# iptables -A OUTPUT -p tcp -d xxxx.95 --tcp-flags ALL PSH,ACK -j DROP

server:
# iptables -A INPUT -p tcp -s xxxx.92 --tcp-flags ALL ACK -j DROP
# iptables -A INPUT -p tcp -s xxxx.92 --tcp-flags ALL PSH,ACK -j DROP
客户端

在这里插入图片描述
root@xxx92:~# netstat -anp | grep .95
tcp 0 81 xxx.xxx.xxx.92:27390 xxx.xxx.xxx.95:8004 ESTABLISHED 1283113/curl
客户端抓包可以看到由于客户端认为连接已经建立成功,会一直重传三次握手后的第一个psh+ack数据包,curl客户端会hang住,而这个连接一直处在ESTABLISHED状态。等待重传一定次数之后,连接会被释放。
如果客户端三次握手后一直不发送数据,显然,这个连接就成为了一个死连接,会一直处于ESTABLISHED状态。只能等待http keepalive超时或者tcp keepalive超时,前者由应用程序如nginx的keepalive_timeout控制,后者可以通过修改这三个内核参数来进行设置:

net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200

可见,如果应用程序没有配置keepalive超时的话,需要等待2个小时内核才会释放连接。

服务端

在这里插入图片描述
服务端的现象和第二次握手丢包一样,由于收不到第三次握手,会进行重传。
值得一提的是synflood其实利用的就是这个原理,发送大量的syn包,然后故意不发送第三次握手的ack包,导致服务端产生大量半连接,从而占用服务端大量资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值