TCP协议理解

三次握手流程

服务端调用 listen() 之后,内核会为这个端口维护两个队列:

  • 半连接队列 (SYN Queue):来存放那些只完成了第一次握手(收到了 SYN),正在等待客户端回复 ACK 的连接。

  • 全连接队列 (Accept Queue): 用来存放那些已经彻底完成了三次握手的连接。这些连接处于 ESTABLISHED 状态,随时可以开始传输数据

三次握手的这个流程如下:

  1. 客户端发送 SYN 报文: 服务端内核收到 SYN,回复 SYN-ACK ,服务器会进入 SYN_RCVD 状态,内核把这个新连接塞进半连接队列
  2. 客户端发送 ACK:服务端内核收到了客户端的最终 ACK,三次握手在内核层面 宣告完成。 内核会做一件重要的事情:把这个连接从"半连接队列"里揪出来,丢进全连接队列
  3. accept() 终于被唤醒并返回:accept() 的唯一工作机制,就是死死盯着"全连接队列"。 一旦内核把完成了握手的连接放进了全连接队列,accept() 就会立刻被唤醒。它从全连接队列里取走这个连接,为你生成一个全新的 Socket 文件描述符(FD),然后成功返回。之后,你的代码就可以用这个新的 Socket 读写数据了。

如果服务端是非阻塞的情况下(非阻塞 Socket (Non-blocking)),会有个例外

如果你把服务端的 listening socket 设置成了非阻塞模式,那么当全连接队列为空时,accept() 不会傻等客户端的 ACK,而是会立刻返回一个 EAGAIN,告诉你"现在没连接,过会儿再来查"。(这也是 Netty、Nginx 等基于 Epoll/Reactor 模型的高性能框架的基石)。

三次握手结论:

  • 服务端的内核需要维护两个队列:因为服务端是被动监听的一方面对四面八方同时涌入的巨量客户端请求,内核必须有地方给它们"排队"
  • 我们在用户空间编写的代码(无论是用 Java Netty、Go、还是 Python)是无法直接触碰这两个队列的,程序只能通过调用系统调用(如 accept())去间接消费全连接队列里的成品。

四次分手过程中 TIME_WAIT 与 CLOSE_WAIT状态

结论先行:"谁主动发起断开,谁就会进入 TIME_WAIT;谁被动接受断开,谁就可能卡在 CLOSE_WAIT。"

首先客户端和服务端是建立好的连接,双端的状态此时都是ESTABLISHED,此时我们做个假设,客户端(Client)主动发起断开连接请求,服务端(Server)被动接受:

    1. 第一次挥手

动作: Client 发送一个 FIN 报文,表示"我没有数据要发了,准备关连接"。

状态: Client 进入 FIN_WAIT_1 状态。

    1. 第二次挥手(也就是针对主动关闭方 FIN 包的 ACK 回复)

动作: Server 收到 FIN,内核回应一个 ACK,表示"我知道了,但我这边可能还有数据没发完,你等我一下"。

状态: * Server 进入 CLOSE_WAIT 状态。 处于"半关闭"状态。上面的ACK是内核自动回复的,但此时 Server 的应用程序还没有调用 close() 关闭 Socket。Client 收到 ACK 后,进入 FIN_WAIT_2 状态。

    1. 第三次挥手

动作: Server 把剩余的数据发完了,应用程序调用 close(),向 Client 发送 FIN 报文,表示"我也发完了,可以正式关了"。

状态: Server 进入 LAST_ACK 状态。

    1. 第四次挥手

动作: Client 收到 Server 的 FIN,回应最后一个 ACK。

状态:Client 进入 TIME_WAIT 状态。

Server 收到 ACK 后,彻底关闭连接(CLOSED)。

Client 在 TIME_WAIT 持续 2MSL(最大报文生存时间)的时间后,确认没事了,才彻底进入 CLOSED 状态。

四次挥手结论:

如果发 SYN :代表你想建立一个新连接

如果发 FIN:代表你想断开连接

通信双方如果有一方想要断开连接,就会发送FIN包,然后进入 FIN_WAIT_1 状态,内核收到 FIN,立刻回一个 ACK(第二次挥手),告诉客户端:"收到,我知道你没数据发了。"

客户端收到 ACK 后,进入 FIN_WAIT_2 状态,静静等待服务端最后发来的 FIN。此时,客户端的发送通道关闭接收通道保持开启 ,此时的服务端在发送ACK 之后处于ClOSE_WAIT 状态。 所以只有被动关闭方才有ClOSE_WAIT状态,意思是内核在等待自家应用程序进行关闭。

相关推荐
boolean的主人1 小时前
超实用!5 个 MySQL 索引优化实战场景(附 10 万测试数据)
后端
BBmmo1 小时前
JDBC基础篇
后端
用户64278006937881 小时前
elpis-core 第一阶段学习心得与收获
后端
kfaino1 小时前
码农的AI翻身·前传 一个大模型从出生到上岗的全过程
后端·aigc
IT_陈寒1 小时前
Vue的这个响应式陷阱让我熬到凌晨三点
前端·人工智能·后端
葫芦和十三2 小时前
图解 MongoDB 17|大集合与工作集:数据超过内存怎么办
后端·mongodb·面试
kfaino9 小时前
码农的AI翻身(三)你好,我叫 Embedding
后端·ai编程
葫芦和十三10 小时前
图解 MongoDB 18|复制集拓扑:Primary、Secondary 和 Arbiter 的分工
后端·mongodb·面试
爱勇宝10 小时前
大多数人不是在使用 AI 赚钱,而是在帮 AI 公司赚钱
前端·后端·程序员