TCP三次握手

第一次握手🤝: 客户端向服务器发送连接请求报文段。无应用层数据。 SYN=1,seq=x(随机) 第二次握手🤝: 服务器端为该TCP连接分配缓存和变量,并向客户端返回确认报文段,允许连接,无应用层数据。 SYN=1,ACK=1,seq=y(随机),ack=x+1 第三次握手🤝: 客户端为该TCP连接分配缓存和变量,并向服务器端返回确认的确认,可以携带数据。 SYN=0,ACK=1,seq=x+1,ack=y+1
为什需要三次,两次不行吗?
假如现在客户端想向服务端进行握手,它发送了第一个连接的请求报文,但是由于网络信号差或者服务器负载过多,这个请求没有立即到达服务端,而是在某个网络节点中长时间的滞留了,以至于滞留到客户端连接释放以后的某个时间点才到达服务端,那么这就是一个失效的报文,但是服务端接收到这个失效的请求报文后,就误认为客户端又发了一次连接请求,服务端就会想向客户端发出确认的报文,表示同意建立连接。 假如不采用三次握手,那么只要服务端发出确认,表示新的建立就连接了。但是现在客户端并没有发出建立连接的请求,其实这个请求是失效的请求,一切都是服务端在自相情愿,因此客户端是不会理睬服务端的确认信息,也不会向服务端发送确认的请求,但是服务器却认为新的连接已经建立起来了,并一直等待客户端发来数据,这样的情况下,服务端的很多资源就没白白浪费掉了。 采用三次握手的办法就是为了防止上述这种情况的发生,比如就在刚才的情况下,客户端不会向服务端发出确认的请求,服务端会因为收不到确认的报文,就知道客户端并没有要建立连接,那么服务端也就不会去建立连接,这就是三次握手的作用
SYN洪泛攻击
SYN泛洪攻击发生在OSI第四层,这种方式利用TCP协议的特性,就是三次握手。 攻击者发送TCP SYN,SYN是TCP三次握手中的第一个数据包,而当服务器返回ACK后,该攻击者就不对其进行再次确认,那这个TCP连接就处于挂起状态,也就是所谓的半连接状态,服务器收不到再确认的话,还会重复发送TCK给攻击者。这样会更加浪费服务器的资源。攻击者就对服务器发送非常大量的这种TCP连接,由于每一个都无法完成三次握手,所以在服务器上,这些TCP连接会因为挂起状态而消耗CPU和内存,最后服务器可能死机,就无法为正常用户提供服务。
为什么 ISN 要随机?
-
防止延迟重复报文被新连接误收;
-
让攻击者无法预测序列号,降低盲劫持风险;
-
在端口快速复用时避免序号空间重叠导致数据错乱。
RFC 6528 要求 32 位中高 24 位必须加密随机。
TCP断联四次挥手

- 第一次挥手👋: 客户端进程发出连接释放FIN报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=x,此时,客户端进入FIN-WAIT-1(终止等待1)状态。
- 第二次挥手👋: 服务端进程收到连接释放FIN报文,发出确认ACK报文,ACK=1,ack=x+1,并且带上自己的序列号seq=y,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。此时,服务端通知高层的应用进程,客户端向服务端的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务端若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。客户端收到服务端的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文,在这之前依然可以接收服务端发送过来的最后的数据。
- 第三次挥手👋: 服务端将最后的数据发送给客户端完成后,就向客户端发送连接释放FIN报文,FIN=1,ack=x+1,此时的序列号为seq=z,此时,服务端就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 第四次挥手👋: 客户端接收到服务端的连接释放FIN报文后,必须发出确认报文,ACK=1,ack=z+1,而自己的序列号是seq=x+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。此时服务端收到客户端发送过来的确认报文,就立即撤销自己的传输控制块TCB,进入CLOSED状态,注意此时的TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,客户端没有收到服务端发来的任何数据,证明服务端已正常关闭,此时客户端会撤销相应传输控制块TCB后,进入CLOSED状态。至此,TCP的连接才真正的断开了。(服务端结束TCP连接的时间要比客户端稍微早一些)
为什么TIME_WAIT状态还需要等2*MSL(Max SegmentLifetime,最大分段生存期)秒之后才能返回到CLOSED状态呢?
TIME_WAIT 状态存在的两大作用
-
保证对方收到最后一个 ACK:若 ACK 丢失,服务器会重传 FIN,客户端必须在 2MSL 内再次 ACK。
-
让旧连接的报文在网络中彻底消失(MSL 为报文最大生存时间),防止同一四元组的新连接收到“脏数据”。
为什么断开连接需要四次挥手👋呢,像建立连接的时候一样,三次行不行呢?
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工 模式,这就意味着,在客户端想要断开连接时,客户端向服务端发送FIN报文,只是表示客户端已经没有数据要发送了,但是这个时候客户端还是可以接收来自服务端的数据(处于半关闭状态)。 当服务端接收到FIN报文,并返回ACK报文,表示服务端已经知道了客户端要断开连接,客户端已经没有数据要发送了,但是这个时候服务端可能依然有数据要传输给客户端(收到一个FIN只意味着在这一方向上没有数据流动)。 当服务端的数据传输完之后,服务端会发送FIN报文给客户端,表示服务端也没有数据要传输了,服务端同意关闭连接,之后,客户端收到FIN报文,立即发送给客户端一个ACK报文,确定关闭连接。在之后,客户端和服务端彼此就愉快的断开了这次的TCP连接。 或许会有疑问,为什么服务端的ACK报文和FIN报文都是分开发送的,但是在三次握手的时候却是ACK报文和SYN报文是一起发送的,因为在三次握手的过程中,当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是在关闭连接时,当服务端接收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,你发的FIN报文我收到了,只有等到服务端所有的数据都发送完了,才能发送FIN报文,因此ACK报文和FIN报文不能一起发送。所以断开连接的时候才需要四次挥手来完成。
比较
| 场景 | 握手次数 | 关键特征 | TCP 状态变化(客户端→服务端) |
|---|---|---|---|
| 三次握手 (simultaneous open 的特例) | 3 条报文 | 建立 单工/全双工 连接 | CLOSED → SYN-SENT → SYN-RCVD → ESTABLISHED |
| 四次挥手 (simultaneous close 的特例) | 4 条报文 | 拆除 全双工 连接,双方各发一次 FIN+ACK | ESTABLISHED → FIN-WAIT-1 → FIN-WAIT-2 → TIME-WAIT / CLOSE-WAIT → LAST-ACK → CLOSED |
| 同时打开 (simultaneous open) | 2 条报文 | 双方同时发 SYN,收到后各回一个 SYN-ACK 即可 | CLOSED → SYN-SENT → 交叉收到 SYN → SYN-RCVD → ESTABLISHED |
| 半关闭 (half-close) | 3 条报文 | 一方只关闭发送方向,仍能继续接收数据 | 主动方:ESTABLISHED → FIN-WAIT-1 → FIN-WAIT-2 → TIME-WAIT;被动方:ESTABLISHED → CLOSE-WAIT → LAST-ACK → CLOSED |
面试一句话总结
三次握手“建双工”,四次挥手“拆双工”;同时开/关是“握手减半”;半关闭“只关发不关收”。