1. TCP到底是什么?网络协议里的C位选手
1.1 先看全局:TCP在TCP/IP协议族中的位置
很多人把“TCP/IP协议”当成一个单独协议,其实它是一整条协议族,从网卡驱动、链路层、网络层、传输层一直到应用层,层层分工。TCP协议处在传输层,往上是HTTP、FTP、MQTT这些应用协议,往下是IP协议。你可以把IP想象成一个快递干线网络,它只负责把一个个数据包从A城市送到B城市,但不保证顺序、不保证不丢;而TCP就是在快递之上加了一个“签收回执系统”——每一件货到了都要回执,没到就重发,顺序乱了就重排。
这也是为什么HTTP、WebSocket、数据库连接、文件传输这些场景几乎全部跑在TCP上。它们都需要完整、按序、不丢的数据流,而UDP只提供“尽力而为”的投递,适合音视频、游戏同步这类丢几帧也问题不大的场景。
1.2 TCP和UDP的区别,以及为什么80%的场合选TCP
拿日常场景打个比方:TCP像打电话,先拨号、接通、确认对方在听,你说一句他“嗯”一声,说完挂断;UDP像对讲机,按下就说,松手就完,对方有没有收到、听没听清,你根本不知道。
从技术参数上讲,差别主要体现在四个方面:
| 维度 | TCP | UDP |
|---|---|---|
| 连接状态 | 面向连接,需建立和释放连接 | 无连接,直接发包 |
| 可靠性 | 确认、重传、排序、去重 | 不可靠,丢包不负责 |
| 传输方式 | 字节流,无边界 | 数据报,有边界 |
| 首部开销 | 20~60字节 | 8字节 |
选型时我的经验是:只要业务对数据完整性敏感,就无脑选TCP。比如设备上报温湿度、PLC读写寄存器、Web接口调用,任何一帧丢了你都没法接受。只有当数据量大、实时性要求极高且能容忍偶尔丢失时,才去考虑UDP或者在此基础上自己实现确认和重传。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 连接是怎么建立的:三次握手与四次挥手
2.1 三次握手,重点在“双方收发能力都确认”
TCP建立连接时,通信双方各发一个SYN包,最后再确认一次。之所以需要三次而不是两次,关键是要确认双方的收发能力都正常。简单说:
- 客户端发SYN,告诉服务端“我要建立连接了”。这一步,服务端能确认客户端的发送能力没问题。
- 服务端回SYN+ACK,告诉客户端“我收到你的请求了,我也准备好了”。这一步,客户端能确认服务端的收发能力都没问题。
- 客户端回ACK,告诉服务端“我也收到你的确认了”。这一步,服务端才能确认客户端的接收能力没问题。
如果只有两次握手,服务端无法确认客户端能不能正常接收。极端情况:客户端发了一个SYN因为网络拥堵延迟了,后来重发一个新的SYN并建立了连接,而那个旧SYN又到达服务端,服务端会认为这是一次新连接并分配资源,此时客户端根本不打算建立这条连接,服务端的资源就被白白占用了。三次握手让服务端在收到第三次ACK后才分配正式的连接资源,能有效避免这种历史重复连接造成的资源浪费。
2.2 四次挥手,重点在“半关闭状态”
断开连接要四次,原因是TCP连接是全双工的,两个方向要分别关闭。四个步骤:
- 主动方发FIN,不再发数据了,但还能收数据。
- 被动方回ACK,表示知道你不再发了,但自己可能还有数据要发。
- 被动方发完剩余数据后,也发FIN,表示“我也没数据了”。
- 主动方回ACK,然后等待一段时间进入CLOSED。
有人问为什么第三步不能和第二步合并?因为被动方在收到FIN后,可能还有业务数据没处理完、没发送完,不能立刻关闭发送方向。所以被动方先回ACK确认,等自己的发送队列清空后再发FIN。这就是所谓的“半关闭”状态,实际应用里,比如客户端要结束上传但还想接收服务端的最终结果,用的就是这种机制。我在做文件传输工具时会主动调用shutdown(SHUT_WR)来关闭发送方向而保留接收方向,这样服务端读完数据后能正常返回结果。
2.3 连接状态机:看到这里你就能看懂netstat了
排查网络问题时,netstat或ss命令会显示各种状态,很多时候报错就藏在状态机里。常用状态大致有11个,我整理了一张对照表:
| 状态 | 含义 | 常见出现位置 |
|---|---|---|
| LISTEN | 服务端正在监听端口 | 服务器 |
| SYN_SENT | 主动方发出SYN,等待回复 | 客户端 |
| SYN_RCVD | 服务端收到SYN并回了SYN+A |
