3. TCP与UDP协议
大家好,我是你们的老朋友。今天我们来聊聊网络编程里最核心的两个协议——TCP和UDP。说实话,我刚开始学网络的时候,觉得这两个东西就是背概念、记端口号。直到后来在项目中踩了坑,才真正明白它们的设计哲学。
嗯,咱们不搞那些虚的。直接上干货。
3.1 TCP三次握手:为什么是三次,不是两次?
TCP是面向连接的协议。什么叫「面向连接」?说白了,就是通信前先打个招呼,确认双方都在线,状态都正常。
这个打招呼的过程,就是三次握手。
三次握手的本质:客户端和服务端互相确认对方的接收能力和发送能力都是正常的。
流程是这样的:
- 第一次握手:客户端发送SYN包(同步序列编号),进入SYN_SENT状态。意思是:「嘿,我想跟你说话,这是我的初始序号X。」
- 第二次握手:服务端收到后,回复SYN+ACK包,进入SYN_RCVD状态。意思是:「收到你的请求了,这是我的初始序号Y,同时确认你的序号X+1。」
- 第三次握手:客户端发送ACK包,进入ESTABLISHED状态。意思是:「好的,我确认收到你的确认了,咱们开始聊吧。」
为什么一定要三次?我举个例子你就明白了。
假设只有两次握手。客户端发了个SYN,服务端回了ACK,服务端就认为连接建立了。但如果这个SYN是网络延迟重发的呢?服务端会傻傻地等数据,客户端却不知道这回事。结果就是服务端资源被白白浪费。
我的经验:我在做高并发服务器时,遇到过SYN Flood攻击。攻击者只发第一次握手,不回复第三次。服务端会一直保留半连接队列,很快内存就爆了。后来我用了SYN Cookie技术,才解决了这个问题。
3.2 TCP四次挥手:好聚好散的艺术
有连接就有断开。TCP的四次挥手,就是优雅地结束对话。
流程是这样的:
- 第一次挥手:主动方发送FIN包,进入FIN_WAIT_1状态。意思是:「我说完了,准备关闭连接。」
- 第二次挥手:被动方回复ACK包,进入CLOSE_WAIT状态。意思是:「收到你的关闭请求,但我可能还有话没说完。」
- 第三次挥手:被动方发送FIN包,进入LAST_ACK状态。意思是:「我也说完了,可以关了。」
- 第四次挥手:主动方回复ACK包,进入TIME_WAIT状态。意思是:「好的,确认收到你的关闭请求。」
这里有个关键点:TIME_WAIT状态会持续2MSL(最大报文生存时间)。为什么要等这么久?
你想想看,如果最后一次ACK丢了,被动方会重发FIN。如果主动方直接关闭了,就收不到这个重发的FIN,被动方就会一直等。所以TIME_WAIT是为了确保被动方能收到最后的ACK。
我曾经踩过的坑:有一次线上服务频繁报错,发现是TIME_WAIT连接太多,把端口耗尽了。后来我调整了内核参数,缩短了TIME_WAIT时间,才恢复正常。嗯,这里要注意,生产环境一定要监控TIME_WAIT的数量。
3.3 TCP流量控制:别发太快,我消化不了
流量控制,说白了就是「接收方告诉发送方:你慢点,我快撑不住了」。
TCP用滑动窗口机制来实现流量控制。接收方会在ACK包里带上自己的窗口大小(win字段),告诉发送方自己还能接收多少数据。
举个例子:
- 接收方说:「我的窗口是4096字节。」
- 发送方就只发4096字节的数据。
- 接收方处理完后,窗口变大了,再告诉发送方:「现在窗口是8192了,可以多发点。」
如果接收方的窗口变成0了,发送方就会停止发送,进入零窗口探测状态。发送方会定期发一个探测包,看看接收方的窗口是不是恢复了。
我的建议:在写网络程序时,如果发现发送速度上不去,先别急着怀疑带宽。检查一下接收方的处理能力,很可能是因为窗口太小导致的。
3.4 TCP拥塞控制:别把网络堵死了
流量控制是点对点的,拥塞控制是全局的。它要解决的是「大家都别发太快,不然整个网络都堵了」的问题。
TCP的拥塞控制有四个阶段:
- 慢启动:刚开始时,拥塞窗口(cwnd)很小,每收到一个ACK,窗口翻倍。指数增长,直到达到慢启动阈值(ssthresh)。
- 拥塞避免:超过ssthresh后,窗口线性增长,每次加1。慢慢试探网络的极限。
- 快速重传:收到3个重复的ACK,说明有包丢了。不等超时,立即重传。
- 快速恢复:发生丢包后,ssthresh减半,cwnd设为ssthresh+3,然后进入拥塞避免阶段。
你可能会问:「为什么丢包就认为是拥塞?」
嗯,这是TCP的默认假设。在传统网络中,丢包的主要原因就是路由器缓存满了。虽然现在有些丢包是无线信号不好导致的,但TCP依然沿用这个假设。
核心思想:TCP的拥塞控制是一种「加法增、乘法减」的策略。网络好时慢慢加,网络差时快速减。
3.5 UDP的特点:简单粗暴,但快
UDP和TCP完全是两个极端。它没有三次握手,没有四次挥手,没有流量控制,没有拥塞控制。说白了,就是「我发我的,你收不收得到是你的事」。
UDP的特点:
- 无连接:不需要建立连接,直接发数据。
- 不可靠:不保证数据到达,不保证顺序。
- 无状态:不维护连接状态,资源消耗小。
- 速度快:没有握手、确认、重传等开销。
UDP的应用场景:
- 视频直播:丢几帧没关系,但延迟必须低。
- 在线游戏:玩家的操作需要实时传输,偶尔丢包可以接受。
- DNS查询:查询包很小,重传成本低。
- 物联网设备:设备资源有限,跑TCP太奢侈。
我的经验:有一次做视频会议系统,一开始用的TCP,结果延迟高得离谱。后来换成UDP,配合FEC(前向纠错)技术,效果好了很多。说白了,选协议要看业务场景,没有银弹。
3.6 TCP与UDP的对比
咱们用一张表来对比一下:
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输,保证顺序 | 不可靠,可能丢包乱序 |
| 流量控制 | 有(滑动窗口) | 无 |
| 拥塞控制 | 有(慢启动、拥塞避免等) | 无 |
| 速度 | 较慢(有握手和确认开销) | 较快(无额外开销) |
| 头部大小 | 20-60字节 | 8字节 |
| 应用场景 | 文件传输、网页浏览、邮件 | 视频直播、游戏、DNS |
3.7 知识体系图
下面这张图,帮你理清本章的知识结构:
好了,TCP和UDP的核心内容就这些。记住一句话:TCP追求可靠,UDP追求速度。选哪个,看你的业务场景。
可可精品资料库