首页 > 技术文章 > TCP的三次握手和四次挥手

ping-pong 2021-12-11 15:46 原文

TCP建立链接的过程需要经历三次握手,TCP断开连接的过程需要四次挥手

一、TCP是传输层协议,对应OSI网络模型的第四层传输层。特点如下:

  1. TCP协议是基于连接的,也就是传输数据前需要先建立好链接,然后再进行传输。
  2. TCP链接一旦建立,就可以在链接上进行双向的通信。
  3. TCP的传输是基于字节流而不是报文,将数据按字节大小进行编号,接收端通过ACK来确认收到的数据编号,通过这种机制,TCP协议能够保证接收数据的有序性和完整性,因此TCP能够提供可靠性传输。
  4. TCP还能提供流量控制的能力,通过滑动窗口来控制数据的发送速率。滑动窗口的本质是动态缓冲区,接收端根据自己的处理能力,在TCP的Header中动态调整窗口的大小,通过ACK应答包通知给发送端,发送端根据窗口大小调整发送的速度。
  5. 仅仅有了流量控制能力还不够,TCP协议还考虑了网络问题可能会导致大量重传,进而导致网络情况进一步恶化,因此TCP协议还提供拥塞控制。TCP处理拥塞控制主要用到了慢启动、拥塞避免、拥塞发生、快速恢复四个算法。

二、详解三次握手建立链接

TCP是基于链接的,所以在传输数据前需要先建立链接,TCP在传输上是双工传输,不区分Client端和Server端,为了便于理解,我们将主动发起建链请求的的那端称为Client端,把被动建立链接的一端称作Server端。****
image
首先建立链接前需要Server端先监听端口,因此Server端建立链接前的初始状态就是LISTEN状态,这时Client端准备建立链接,先发送一个SYN同步包,发送完同步包后,Client端的链接状态变成了 SYN_SENT 状态。Server 端收到 SYN 后,同意建立链接,会向 Client 端回复一个 ACK。

由 于 TCP 是 双 工 传 输 , Server 端 也 会 同 时 向 Client 端 发 送 一 个 SYN , 申 请 Server 向 Client 方向建立链接。发送完 ACK 和 SYN 后,Server 端的链接状态就变成了 SYN_RCVD。

Client 收到 Server 的 ACK 后,Client 端的链接状态就变成了 ESTABLISHED 状态。同时,Client 向 Server 端发送 ACK,回复 Server 端的 SYN 请求。

Server 端收到 Client 端的 ACK 后,Server 端的链接状态也就变成了的 ESTABLISHED 状态,此时建连完成,双方随时可以进行数据传输。

大家想必都听过SYN泛洪攻击,这个是发生在服务端收到Client的SYN请求后,发送了ACK和SYN,但是Client端不进行回复。导致Server端大量的链接处于SYN_RCVD状态,进而影响其他正常请求的建连。

那么如何防范这种攻击呢?

1、修改等待数
sysctl -w net.ipv4.tcp_max_syn_backlog=2048

2、启用syncookies
sysctl -w net.ipv4.tcp_syncookies=1

3、修改重试次数
sysctl -w net.ipv4.tcp_syn_retries = 0
重传次数设置为0,只要收不到客户端的响应,立即丢弃该连接,默认设置为5次

4、限制单IP并发数
使用iptables限制单个地址的并发连接数量:
iptables -t filter -A INPUT -p tcp --dport 80 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 10 --connlimit-mask 32 -j REJECT

5、限制C类子网并发数
使用iptables限制单个c类子网的并发链接数量:
iptables -t filter -A INPUT -p tcp --dport 80 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 10 --connlimit-mask 24 -j REJECT

6、限制单位时间内连接数
设置如下:
iptables -t filter -A INPUT -p tcp --dport 80 -m --state --syn -m recent --set iptables -t filter -A INPUT -p tcp --dport 80 -m --state --syn -m recent --update --seconds 60 --hitcount 30 -j DROP

7、修改modprobe.conf
为了取得更好的效果,需要修改/etc/modprobe.conf
options ipt_recent ip_list_tot=1000 ip_pkt_list_tot=60
作用:记录10000个地址,每个地址60个包,ip_list_tot最大为8100,超过这个数值会导致iptables错误

8、限制单个地址最大连接数
iptables -I INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j D

三、详解四次握手断开链接

image
TCP 链接的关闭,通信双方都可以先发起,我们暂且把先发起的一方看作 Client,从图中 看出,通信中 Client 和 Server 两端的链接都是 ESTABLISHED 状态,然后 Client 先主动 发起了关闭链接请求,Client 向 Server 发送了一个 FIN 包,表示 Client 端已经没有数据要发送了,然后 Client 进入了 FIN_WAIT_1 状态。

Server 端收到 FIN 后,返回 ACK,然后进入 CLOSE_WAIT 状态。此时 Server 属于半关 闭状态,因为此时 Client 向 Server 方向已经不会发送数据了,可是 Server 向 Client 端可能还有数据要发送。

当 Server 端数据发送完毕后,Server 端会向 Client 端发送 FIN,表示 Server 端也没有数 据要发送了,此时 Server 进入 LAST_ACK 状态,就等待 Client 的应答就可以关闭链接了。

Client 端 收 到 Server 端 的 FIN 后 , 回 复 ACK , 然 后 进 入 TIME_WAIT 状 态 。TIME_WAIT 状态下需要等待 2 倍的最大报文段生存时间,来保证链接的可靠关闭,之后才会进入 CLOSED 关闭状态。而 Server 端收到 ACK 后直接就进入 CLOSED 状态。

疑问1:问为什么需要等待 2 倍最大报文段生存时间之后再关闭链接?
原因是:

  1. 保证 TCP 协议的全双工连接能够可靠关闭;
  2. 保证这次连接的重复数据段从网络中消失,防止端口被重用时可能产生数据混淆。

疑问2:为啥建立链接需要3次,断开链接需要4次?
原因:
从这个交互流程可以看出,无论是建连还是断链,都是需要在两个方向上进行,只不过建连时,Server 端的 SYN 和 ACK 合并为一次发送,而断链时,两个方向上数据发送停止的时 间可能不同,所以不能合并发送 FIN 和 ACK。这就是建连三次握手而断链需要四次的原因。

参考网址服务器遭到SYN攻击怎么办?如何防御SYN攻击?

推荐阅读