本文正在参加「金石计划 . 瓜分6万现金大奖」
三次握手 四次挥手
最近逛了逛各平台面经,发现计算机基础考察依旧很深,但是大家的认知似乎都停留在“八股”的层面上,只是简简单单一背,没有任何的理解就向面试官一同输出。所以本文将带你深入理解三次握手四次挥手的原理,让你和其他人拉开差距!
三次握手
- 一开始,客户端和服务端都处于关闭状态。先是服务端主动监听某个端口
- 然后客户端随机初始化序号(
client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把SYN标志位置为1,表示SYN报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于SYN-SENT状态。
- 服务端收到客户端的
SYN报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入client_isn + 1, 接着把SYN和ACK标志位置为1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于SYN-RCVD状态。
- 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部
ACK标志位置为1,其次「确认应答号」字段填入server_isn + 1,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于ESTABLISHED状态。 - 服务器收到客户端的应答报文后,也进入
ESTABLISHED状态。
第三次握手是可以携带数据的,前两次握手是不可以携带数据的!
为什么是三次
原因一:避免历史连接(首要原因)
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵情况下:
- 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
- 那么此时服务端就会回一个
SYN + ACK报文给客户端; - 客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送
RST报文给服务端,表示中止这一次连接。
原因二:同步双方初始序列号
TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:
- 接收方可以去除重复的数据;
- 接收方可以根据数据包的序列号按序接收;
- 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道);
这样一来一回,才能确保双方的初始序列号能被可靠的同步。
原因三:避免资源浪费
如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
四次挥手
- 客户端打算关闭连接,此时会发送一个 TCP 首部
FIN标志位被置为1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态。 - 服务端收到该报文后,就向客户端发送
ACK应答报文,接着服务端进入CLOSED_WAIT状态。 - 客户端收到服务端的
ACK应答报文后,之后进入FIN_WAIT_2状态。 - 等待服务端处理完数据后,也向客户端发送
FIN报文,之后服务端进入LAST_ACK状态。 - 客户端收到服务端的
FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态 - 服务器收到了
ACK应答报文后,就进入了CLOSED状态,至此服务端已经完成连接的关闭。 - 客户端在经过
2MSL一段时间后,自动进入CLOSED状态,至此客户端也完成连接的关闭。
为什么需要 TIME_WAIT 状态?
主动发起关闭连接的一方,才会有 TIME-WAIT 状态。
需要 TIME-WAIT 状态,主要是两个原因:
- 防止历史连接中的数据,被后面相同四元组的连接错误的接收,让两个方向上的数据包都被丢弃
- 保证「被动关闭连接」的一方,能被正确的关闭;
为什么 TIME_WAIT 等待的时间是 2MSL?
MSL 是 报文最大生存时间
MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。
TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 FIN 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。
可以看到 2MSL时长 这其实是相当于至少允许报文丢失一次。比如,若 ACK 在一个 MSL 内丢失,这样被动方重发的 FIN 会在第 2 个 MSL 内到达,TIME_WAIT 状态的连接可以应对。
2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。
后面还会继续更深入计算机基础的文章,本篇就告一段落!