TCP三次握手和四次挥手详解

TCP 的三次握手和四次挥手是大厂常见的面试考题,具有一定的水平区分度。

如果你的回答不符合面试官期待的水准,有可能就直接凉凉。

目录

三次握手

为什么两次握手不可以

1. 确认双方的收发能力

2. 序列号可靠同步

3. 阻止重复历史连接的初始化

4. 安全问题

什么是半连接队列?

SYN Flood攻击

三次握手可以携带数据吗?

TCP 四次挥手

为什么建立连接握手三次,关闭连接时需要是四次呢?

为什么TIME_WAIT 状态需要经过 2MSL 才能返回到 CLOSE 状态?

状态机:12种


三次握手

TCP 三次握手,其实就是建立一个 TCP 连接。客户端与服务器交互需要 3 个数据包。

作用:确认双方的接收和发送能力是否正常,初始序列号,交换窗口大小以及 MSS 等信息。

TCP三次握手和四次挥手详解_第1张图片

 

第一次握手:客户端发送 SYN 报文,并进入 SYN_SENT 状态,等待服务器的确认;

第二次握手:服务器收到 SYN 报文,需要给客户端发送 ACK 确认报文,同时服务器也要向客户端发送一个 SYN 报文,所以也就是向客户端发送 SYN + ACK 报文,此时服务器进入 SYN_RCVD 状态;

第三次握手:客户端收到 SYN + ACK 报文,向服务器发送确认包,客户端进入 ESTABLISHED 状态。待服务器收到客户端发送的 ACK 包也会进入 ESTABLISHED 状态,完成三次握手。

TCP 连接本质上就是双方各自维护所需的状态状态,所以 TCP 状态机是 TCP 的核心内容,学习 TCP 一定要搞懂这些状态机之间的转换。

为什么两次握手不可以

1. 确认双方的收发能力

TCP 建立连接之前,需要确认客户端与服务器双方的收包和发包的能力。

1.  第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

2. 第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。

不过此时服务器并不能确认客户端的接收能力是否正常。

3. 第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

所以,只有三次握手才能确认双方的接收与发送能力是否正常。

2. 序列号可靠同步

如果是两次握手,服务端无法确定客户端是否已经接收到了自己发送的初始序列号,

如果第二次握手报文丢失,那么客户端就无法知道服务端的初始序列号,

那 TCP 的可靠性就无从谈起。

3. 阻止重复历史连接的初始化

客户端由于某种原因发送了两个不同序号的 `SYN` 包,我们知道网络环境是复杂的,旧的数据包有可能先到达服务器。

如果是两次握手,服务器收到旧的 `SYN` 就会立刻建立连接,那么会造成网络异常。

如果是三次握手,服务器需要回复 `SYN+ACK` 包,客户端会对比应答的序号,如果发现是旧的报文,就会给服务器发 `RST` 报文,直到正常的 `SYN` 到达服务器后才正常建立连接。

所以三次握手才有足够的上下文信息来判断当前连接是否是历史连接。

4. 安全问题

我们知道 TCP 新建连接时,内核会为连接分配一系列的内存资源,如果采用两次握手,就建立连接,那会放大 DDOS 攻击的。

TCP 作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而三次握手恰好可以满足以上两方面的需求!

什么是半连接队列?

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立连接。服务器会把这种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。

当然还有一 全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

SYN Flood攻击

ddos攻击的一种

Tcp三次握手         第一个包SYN正常情况服务器回SYN ACK包  正常用户回ACK

如果是一个黑客   第三个包(ACK)不给他回    服务器就会等你回(这就是半连接)

半连接数量过多的时候      就会耗尽设备的资源 服务器就崩掉了(无法处理正常业务)

三次握手可以携带数据吗?

第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。

假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,疯狂着重复发 SYN 报文,这会让服务器花费大量的内存空间来缓存这些报文,这样服务器就更容易被攻击了。

对于第三次握手,此时客户端已经处于连接状态,他已经知道服务器的接收、发送能力是正常的了,所以是可以携带数据的。

TCP 四次挥手

应用程序不需要数据通信了,就会发起断开 TCP 连接。

建立一个连接需要三次握手,终止一个连接需要经过四次挥手。

TCP三次握手和四次挥手详解_第2张图片

第一次挥手。客户端发起 FIN 包(FIN = 1),客户端进入 FIN_WAIT_1 状态。TCP 规定,即使 FIN 包不携带数据,也要消耗一个序号

第二次挥手。服务器端收到 FIN 包,发出确认包 ACK(ack = u + 1),并带上自己的序号 seq=v,服务器端进入了 CLOSE_WAIT 状态。这个时候客户端已经没有数据要发送了,不过服务器端有数据发送的话,客户端依然需要接收。客户端接收到服务器端发送的 ACK 后,进入了 FIN_WAIT_2 状态。

第三次挥手。服务器端数据发送完毕后,向客户端发送 FIN 包(seq=w ack=u+1),半连接状态下服务器可能又发送了一些数据,假设发送 seq 为 w。服务器此时进入了 LAST_ACK 状态。

第四次挥手。客户端收到服务器的 FIN 包后,发出确认包(ACK=1,ack=w+1),此时客户端就进入了 TIME_WAIT 状态。注意此时 TCP 连接还没有释放,必须经过 2*MSL 后,才进入 CLOSED 状态。而服务器端收到客户端的确认包 ACK 后就进入了 CLOSED 状态,可以看出服务器端结束 TCP 连接的时间要比客户端早一些。

为什么建立连接握手三次,关闭连接时需要是四次呢?

其实在 TCP 握手的时候,接收端发送 `SYN+ACK` 的包是将一个 `ACK` 和一个 `SYN` 合并到一个包中,所以减少了一次包的发送,三次完成握手。

对于四次挥手,因为 TCP 是全双工通信,在主动关闭方发送 FIN 包后,接收端可能还要发送数据,不能立即关闭服务器端到客户端的数据通道,所以也就不能将服务器端的 `FIN` 包与对客户端的 `ACK` 包合并发送,只能先确认 `ACK`,然后服务器待无需发送数据时再发送 `FIN` 包,所以四次挥手时必须是四次数据包的交互。

为什么TIME_WAIT 状态需要经过 2MSL 才能返回到 CLOSE 状态?

`MSL` 指的是报文在网络中最大生存时间。在客户端发送对服务器端的 `FIN` 的确认包 `ACK` 后,这个 `ACK` 包是有可能不可达的,服务器端如果收不到 `ACK` 的话需要重新发送 `FIN` 包。

所以客户端发送 `ACK` 后需要留出 `2MSL` 时间(ACK 到达服务器 + 服务器发送 FIN 重传包,一来一回)等待确认服务器端确实收到了 ACK 包。

也就是说客户端如果等待 `2MSL` 时间也没有收到服务器端的重传包 `FIN`,说明可以确认服务器已经收到客户端发送的 `ACK`。

还有第 *2* 个理由,避免新旧连接混淆。

在客户端发送完最后一个 `ACK` 报文段后,在经过 `2MSL` 时间,就可以使本连接持续的时间内所产生的所有报文都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文。

你要知道,有些自作主张的路由器会缓存 IP 数据包,如果连接重用了,那么这些延迟收到的包就有可能会跟新连接混在一起。

状态机:12种

1. CLOSED:不在连接状态(这是为方便描述假想的状态,实际不存在)

2. LISTEN:等待从任何远端TCP 和端口的连接请求。

3. SYN_SENT:发送完一个连接请求后等待一个匹配的连接请求。

4. SYN_RECEIVED:发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。

5. ESTABLISHED:表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。

6. FIN_WAIT_1:等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。

7. FIN_WAIT_2:等待远端TCP 的连接终止请求。

8. CLOSE_WAIT:等待本地用户的连接终止请求。

9. CLOSING状态:等待远端TCP 的连接终止请求确认。

客户端发送了FIN,但是没有收到服务器的ACK,却收到了服务器的FIN,这种情况发生在服务器发送的ACK丢包的时候,因为网络传输有时会有意外。

10. LAST_ACK:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认)

11. TIME_WAIT:等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。

12. TIME_WAIT 两个存在的理由:•可靠的实现tcp全双工连接的终止;•允许老的重复分节在网络中消逝。

TCP三次握手和四次挥手详解_第3张图片

1.一开始,建立连接之前服务器和客户端的状态都为CLOSED;

2.服务器创建socket后开始监听,变为LISTEN状态;

3.客户端请求建立连接,向服务器发送SYN报文,客户端的状态变味SYN_SENT;

4.服务器收到客户端的报文后向客户端发送ACK和SYN报文  此时服务器的状态变为SYN_RCVD;

5.然后,客户端收到ACK、SYN,就向服务器发送ACK,客户端状态变为ESTABLISHED;

6.服务器端收到客户端的ACK后变为ESTABLISHED。此时3次握手完成,连接建立! 

TCP三次握手和四次挥手详解_第4张图片

 

由于TCP连接是全双工的,断开连接会比建立连接麻烦一些。

1.客户端先向服务器发送FIN报文,请求断开连接,其状态变为FIN_WAIT1;

2.服务器收到FIN后向客户端发送ACK,服务器的状态围边CLOSE_WAIT;

3.客户端收到ACK后就进入FIN_WAIT2状态,此时连接已经断开了一半了。如果服务器还有数据要发送给客户端,就会继续发送;

4.直到发完数据,就会发送FIN报文,此时服务器进入LAST_ACK状态;

5.客户端收到服务器的FIN后,马上发送ACK给服务器,此时客户端进入TIME_WAIT状态;

6.再过了2MSL长的时间后进入CLOSED状态。服务器收到客户端的ACK就进入CLOSED状态。

至此,还有一个状态没有出来:CLOSING状态。

你可能感兴趣的