找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2495

积分

0

好友

351

主题
发表于 4 天前 | 查看: 18| 回复: 0

即便是对网络协议了解不多的朋友,第一反应可能也是肯定的。这背后的原因,大家或许也能说个大概。

TCP协议卡通人物背影与幽默对话框

但这就引出了更深层的问题:使用UDP就一定比TCP快吗?是否存在UDP反而比TCP慢的场景? 我们今天就来深入探讨一下。

使用socket进行数据传输

当开发者需要在A电脑的进程发送数据到B电脑的进程时,通常会使用socket编程。Socket可以看作是一个电话或者邮箱。当你发送消息时,就像拨打电话或将信件投入邮箱,操作系统内核会通过Socket自动完成数据的传输过程。

通过Socket,我们可以选择基于TCP或UDP协议进行通信。

TCP作为一种可靠性协议,每次发送消息后都能得到明确的确认,类似于打电话时的“喂喂”,能立刻知道对方是否在线接听。

而UDP则像是给邮政信箱寄信,信件寄出后,你无法确认对方是否成功收到,丢失的可能性是存在的。

回到技术层面,创建Socket的典型方式如下:

fd = socket(AF_INET, 具体协议, 0);

注意这里的“具体协议”。如果传入 SOCK_STREAM,表示使用字节流传输数据,即 TCP协议

TCP协议核心特性:面向连接、可靠、基于字节流

如果传入 SOCK_DGRAM,则表示使用数据报传输,也就是 UDP协议

UDP协议核心特性:无连接、不可靠、基于消息报

函数返回的 fd 是Socket的文件描述符,可以理解为Socket的唯一标识。通过这个 fd,内核可以找到对应的Socket数据结构。

发送消息时,只需操作这个 fd,例如执行 send(fd, msg, ...)。接收方则执行 recv(fd, msg, ...) 来获取数据。在网络/系统编程中,理解Socket和文件描述符是基础。

UDP数据报发送与接收过程动态示意图

对于异常情况的处理

但如果传输过程不顺利呢? 比如,数据包丢失了怎么办?

UDP和TCP对此的态度截然不同。

UDP的态度基本是:“哦,丢了?那不关我的事。”
TCP则截然相反:“这不行!是不是我发太快了?还是网络拥堵?放心,我一定给你重发。”

TCP为了实现这种可靠性承诺,在背后默默做了大量工作。

重传机制

TCP为每个发出的数据包分配一个序列号。接收方收到后,会回复一个确认号。发送方通过确认号就能知道哪些数据包已被成功接收。

如果长时间未收到某个数据包的确认,TCP会认为该包已丢失,并重新发送,这就是重传机制

TCP丢包与重传示意图

流量控制机制

然而,重传对性能影响较大,属于不得已而为之的下策。因此,TCP需要尽力避免重传的发生。

考虑到发送方和接收方的数据处理能力可能不同,TCP引入了发送窗口和接收窗口接收窗口大小表示接收方当前还能处理多少数据,发送窗口大小则限制了发送方当前能发送的数据量。TCP通过动态调整窗口大小来控制发送速率,从而有效降低丢包概率。

TCP流量控制与滑动窗口机制示意图

滑动窗口机制

接收方的处理能力是动态变化的,有时快有时慢。处理快时希望接收更多数据,处理慢时则希望对方发送慢一些。发送过多数据可能导致处理不过来而丢包,进而触发重传。因此,接收窗口的大小需要能够动态调整,这就是滑动窗口机制

简单来说,TCP通过滑动窗口机制来实现流量控制

TCP滑动窗口动态变化过程

拥塞控制机制

但问题不仅限于通信双方。网络环境的拥堵也可能导致丢包。可以将网络想象成一条公路,当路上挤满了其他车辆时,即使你家有5辆车且目的地有5个车位,也无法一次性全部上路。

TCP希望能感知外部网络状况,并据此调整发包速率。其策略是“慢启动,拥塞避免”:先发送少量数据试探,然后逐渐增加发送量,直到出现丢包。此时,TCP就大致知道了当前网络的承载能力,并以此调整后续的发送节奏,这就是拥塞控制机制

简单区分:流量控制针对的是单个连接两端的数据处理能力;拥塞控制针对的是整个网络路径的传输能力。

用停车场比喻网络拥塞控制

分段机制

尽管上述机制旨在降低丢包率,但重传仍无法完全避免。那么,当重传发生时,如何减少其影响呢?

答案是分段。如果需要发送一个超大数据包,一旦丢失,就需要重传整个大包,代价高昂。如果将其分割成多个小段,那么丢失时只需重传丢失的那一小段即可,压力骤减。这就是TCP的分段机制

这一小段的长度,在传输层称为 MSS。当应用层下发的数据长度大于MSS时,TCP层会将其分成多个小于等于MSS的TCP段。

数据包在传输层根据MSS进行分段

数据包到达网络层(IP层)后,如果其长度仍大于 MTU,IP层还会继续分片。

数据包在网络层根据MTU进行分片

通常情况下,MSS = MTU - 40字节(20字节IP头+20字节TCP头)。因此,经过TCP分段的数据,到了IP层一般就不会再被分片了

MSS与MTU的区别及数据包结构

乱序重排机制

数据包既会被分段,网络路径又复杂多变,乱序也就成了常见现象。例如,发送顺序为1,2,3的数据包,可能因为路径不同,以2,3,1的顺序到达。

TCP利用数据包的序列号来识别其原始顺序。对于先到达的后发数据包,TCP会将其暂存到乱序队列中。待前面的数据包都到达后,再按照正确顺序重新组装并交付给应用层,这就是乱序重排机制

网络传输导致乱序及乱序队列处理

连接机制

我们常说UDP无连接,TCP面向连接。这里的“连接”究竟是什么?

TCP通过上述种种机制实现了可靠传输,这些机制的背后,是操作系统内核在两端维护的一套复杂状态机(用于处理三次握手、四次挥手、各种异常等)。这套状态机就是所谓的“连接”。而UDP无需维护这套状态机,因此它是“无连接”的。

网络链路漫长而复杂,数据包丢失是常态。我们日常使用TCP传输数据时对此毫无感知,正是因为TCP默默承担了所有复杂性。

这就是TCP三大特性——“面向连接、可靠、基于字节流”中“可靠”一词的含义。如果换成UDP,丢包就是真丢了。

用UDP就一定比用TCP快吗?

此时UDP可能会反驳:“正因为我没有这些复杂的可靠性机制,所以我天生就快啊!

在绝大多数情况下,这确实是事实。但问题也随之而来:

有没有使用了UDP,性能却反而不如TCP的场景呢?

答案是:有。在探讨这个场景前,我们需要先了解UDP的常见用途。

实际上,很少有项目会直接在生产环境中使用“裸UDP”。UDP的核心价值,在于它为内核提供了一个最基础、最轻量的网络传输原语

正因为“忌惮”UDP的丢包问题,许多基于UDP的协议都会在应用层实现各自的可靠性保证机制。例如,游戏常用的KCP协议,以及新兴的QUIC协议,都在UDP之上实现了重传等逻辑,构建了一套类似TCP的可靠性体系。

教科书常说UDP适合音视频传输,因为这类场景可以容忍部分丢包。但并非所有包都能丢,比如关键帧丢失会导致画面模糊或卡顿,此时仍需应用层重传。此外,应用层通常也会实现乱序处理。例如,在网络通话中,你可能会听到断断续续的语句,但绝不会听到词语顺序完全错乱的话。

所以,虽然选择了UDP,但应用层往往会自行实现一部分可靠性机制

关键问题来了:当需要传输一个超大数据包时,会发生什么?

对于TCP,其内部会根据MSS自动分段。分段后的每个数据包大小不超过MTU,因此IP层通常不会再分片。此时若发生丢包,只需重传丢失的那个MSS分段即可。

TCP自动分段传输与重传过程

对于UDP,其本身不具备分段功能。如果应用层下发的数据包过大,到达IP层时就会被分片。此时一旦某个IP分片丢失,就需要重传整个原始的大数据包(因为UDP和IP层都没有记录分片与原始数据包的完整映射关系,无法只重传丢失的片段)。

UDP数据包在IP层分片及整体重传过程

在这种情况下,使用UDP反而会比TCP慢

当然,解决方案也很直接:在应用层实现类似的分段机制。只要UDP之上的应用协议能够将大数据分割成适合网络传输的块,并管理它们的重传,就能避免上述性能问题。

总结

  • TCP为了实现可靠性,引入了重传、流量控制、滑动窗口、拥塞控制、分段及乱序重排等一系列复杂机制,这通常使其在单纯的数据传输速度上慢于“轻装上阵”的UDP。
  • TCP是面向连接的协议,UDP是无连接的。这里的“连接”本质上是操作系统内核维护的一套复杂状态机。
  • 大部分实际项目会在UDP基础上,模仿TCP实现不同程度的可靠性机制,例如KCP协议就在应用层实现了重传。
  • 在传输超大数据包且未实现应用层分段的情况下,UDP数据包会在IP层被分片。此时丢包会导致整个大数据包重传,性能可能反而不如具备自动分段能力的TCP。

深入了解这些底层机制,有助于我们在不同场景下做出更合理的协议选择。如果你想进一步探讨网络协议或其他技术话题,欢迎在云栈社区交流分享。

熊猫开心表情包配文“唯唯诺诺”




上一篇:苹果MacBook Pro M5新品动态:官网发货延迟至3月,预示芯片升级
下一篇:2026年设计趋势预测:AI人机共创与品牌动态识别等7大方向剖析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 02:54 , Processed in 0.370095 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表