TCP 三次握手、四次挥手

发布于:2025-09-10 ⋅ 阅读:(20) ⋅ 点赞:(0)

三次握手

三次握手形象版,快速理解

deepseek 的象形比喻:三次握手建立连接就像打电话一样:
(1) A 打给 B,“喂, 你能听到我说话吗?”
(2) B 回答 A,“嗯,可以听到,你能听到我说话吗?”
(3) A 回答 B,“没问题,可以听到,我们开始聊天吧!”
客户端请求服务器的过程,可以将 A 当作客户端,B 当作服务器,它们进行“握手”的目的是证明自己的接收能力和发送能力都是正常的。
第一次握手,客户端向服务器发送消息,服务器收到消息后,服务器知道了:客户端的发送能力和服务器的接收能力没问题
第二次握手,服务器向客户端发送消息,客户端收到消息后,客户端知道了:客户端的发送能力、服务器的接收能力、服务器的发送能力、客户端的接收能力都是没问题的。但此时服务器不知道自己的发送能力和客户端的接收能力是否正常
第三次握手,客户端向服务器发送消息,服务器收到消息后,服务器就知道:自己的发送能力和客户端的接收能力也没问题(在第一次握手的时候,服务器已经知道了自己的接收能力和客户端的发送能力没问题)
三次握手结束,客户端和服务器建立连接成功!

三次握手详细过程

在 Tcp 报文头部,有下面几个比较关键的字段:
SYN:同步序列号,是一个控制位,当它为 1 时,表示这是一个用于建立连接的报文
ACK:也是一个控制位,当它位 1 时,表示报文中的ack字段是有效的,除了第一个 SYN 包,后续所有 ACK 位都必须是 1
seq:序列号,标识本报文所发送数据的第一个字节的编号(TCP 是可靠传输,每个字节都有编号,确保数据有效不丢失)
ack:确认号,期望收到对方下一个报文段的第一个数据字节的序列号,同时表示ack-1之前的所有数据都已正确收到
第一步,客户端发送 SYN: 设置 SYN 标识位为1,并随机生成一个初始序列号 x,放在 seq 字段里(服务器你好,我想和你建立连接,我数据的初始序列号为 x)
第二步,服务器回复 SYN-ACK: 服务器收到报文后,同意建立连接,于是回复一个报文,SYN-ACK 包,这个包将 TCP 标志位的 SYN 和 ACK 都设置为 1,同时,服务器也随机生成自己的初始序列号 y,放在 seq 字段里,同时需要确认客户端的 SYN 包。确认方法:将客户端发来的序列号 x 加 1 作为 ack 的值发回去(客户端你好,我同意建立连接,我数据的初始序列号是 y,并且我已经收到你的序列号 x,接下来期望从你的第 x+1 个字节的数据开始接收)
第三步,客户端发送 ACK: 客户端收到服务器的 SYN-ACK 包后,连接基本已经建立,客户端需要发送最后一个确认包,将 ACK 标志位设置为1,seq 字段此时变为了 x+1,因为第一步的 SYN 包会消耗一个字节,同时,客户端需要确认服务器的消息,因此,将 ack 字段设置为 y+1 发送回去(服务器,我已收到你的确认,接下来建立连接吧!接下来,客户端发送的数据将从 x+1 开始,并且期望服务器从第 y+1 个字节的序列开始接收)

注意:x 和 y 都是随机生成的,因此 x+1 和 y+1 只是一个象征性的编号,意思是将客户端发送数据的起始位置命名为编号 x+1,服务器要发送的数据起始位置命名为编号 y+1。

三次握手之后,客户端和服务端都会进入 ESTABLISHED 状态

四次挥手

四次挥手象形理解

A 和 B 打电话,要礼貌的结束通话
(1) A 对 B 说,“我没什么要说的了,我要挂了”(客户端发送消息通知服务器,我的消息发送完了,准备断开连接了)
(2) B 回答 A,“ok 我知道你说完了,但是稍等,我还有话要说”(服务器收到客户端要结束连接的请求,但服务器的消息还没有发送完,此时客户端——>服务器的通道关闭,客户端不能再给服务器发消息,但是服务器依然可以给客户端发)
(3) B 对 A 说,“好了,我说完了,我要挂了!”(服务器消息发送完毕)
(4) A 对 B 说,“好的,我们挂了吧。”(双方通道全部关闭)

四次挥手详细过程

第一步,客户端发送 FIN: 将 FIN 标志位设置位 1,这是连接结束的信号,报文中的 seq 字段为 u,u 表示客户端已发送的数据最后一个字节的序列号加 1,客户端进入 FIN-WAIT-1 状态(客户端对服务器说,我没数据给你了,我要关闭我到你那边的发送通道了)
第二步,服务器回复 ACK: 服务器收到 FIN 包后,发送一个确认包,ACK 的标志位设为 1,将客户端发来的序列号加1,即将 ack 的值设置为 u+1,并发送回去,此时,客户端进入 CLOSE-WAIT状态,因为客户端->服务器关闭,但服务器->客户端未关闭,此时,服务器进入半关闭Half-Colse状态 (服务器确认客户端即将关闭连接通道,进入半关闭状态)
第三步,服务器发送 FIN: 服务器向客户端发送完消息了,也准备关闭连接。将 FIN 和 ACK 字段都设置为 1,seq 字段设置为 w,w 是服务器之前已经最后一个字节的序列号 + 1,ack 字段依然是 u+1,服务器进入LAST-ACK状态(服务器发送完消息,通知客户端,即将关闭发送通道)
第四步,客户端回复 ACK: 客户端收到服务器 FIN 包后,知道服务器发送完了,于是向服务器发送最后一个确认包,ACK 标志位设为1,将 ack 设置为服务器发来的序列号 w 加1,即 ack = w+1,客户端进入 TIME-WAIT状态,时间为2MSL(两倍的最大报文段生存时间)当 TIME-WAIT 状态结束后,变为 CLOSED,服务器收到这个 ACK 包后,释放资源,状态变为 CLOSED。如果服务器没有收到 ACK,它就会再次发送 FIN,此时处于 TIME-WAIT 状态的客户端还能再次发送 ACK,这就是 TIME-WAIT 状态存在的意义!

为什么不是三次挥手?
因为TCP是全双工的,允许在两个方向上独立传输,所以 TCP 可以半关闭,需要耗费一个来回


网站公告

今日签到

点亮在社区的每一天
去签到