资讯详情

TCP vs UDP:传输层协议原理、选型与排障实践

📅 2026/10/21 3:23:59 | 华诺云谱 👁 阅读
TCP vs UDP:传输层协议原理、选型与排障实践
做了这么多年网络相关的开发和排障工作被新手问得最多的还是那个老问题传输层的两个主要协议到底怎么区分TCP 和 UDP 这两个名字几乎人人会说但再往下追问一句“它们各自到底干了什么为什么这么设计”很多人都开始含糊了。市面上讲计算机网络基础的资料不少但大多停留在“TCP 可靠、UDP 不可靠”这种口号层面真正能把协议设计和实际场景串起来的却不多。这篇文章我打算把传输层这两个主要协议从头到尾拆一遍从它们要解决的问题讲起再深入到握手、窗口、重传这些机制最后落到项目选型和排查问题的实操上适合正在补网络基础的同学也适合写 Socket 程序时需要跟连接状态打交道的前后端开发。我尽量不用教科书式的口吻而是以这些年实际调网络问题的经验来讲每一步都解释清楚“为什么”。如果你能跟着读完下次再有人问你 TCP 和 UDP 的区别你应该能讲出比“一个可靠一个快”更有层次的东西。1. 传输层在忙什么一台主机上那么多进程数据到底交给谁1.1 网络层的局限与端口号的诞生理解 TCP 和 UDP 之前得先搞清楚传输层在整个网络体系里的位置。网络层IP 协议那一层解决的核心问题是“把数据从一台主机送到另一台主机”它靠 IP 地址定位设备。但这只是第一步——一台服务器上同时跑着 Web 服务、数据库服务、监控代理、日志采集器数据到了这台机器的网卡之后内核怎么知道该把它交给哪个进程答案是端口号。传输层在 IP 地址的基础上引入了 16 位的端口号范围 0 到 65535。一个完整的连接由四个要素唯一确定源 IP、源端口、目标 IP、目标端口这就是常说的四元组。这样设计之后一台主机上的多个进程可以各自占用不同的端口互不干扰地收发数据。你可以做一个不太严谨但很好理解的类比IP 地址相当于给一栋写字楼的地址快递员负责把包裹送到楼门口但楼里有几十家公司包裹必须由前台按照“分机号”转交给具体的人。这里的运输层就是前台端口号就是分机号。没有这个环节网络层只能做到“送到楼门口”无法完成端到端的进程通信。端口号还分了几个区间0 到 1023 是知名端口比如 HTTP 的 80、HTTPS 的 443、DNS 的 531024 到 49151 是注册端口给常见应用使用49152 到 65535 是动态或私有端口客户端发起连接时操作系统会从这段临时分配一个源端口。1.2 传输层要回答的两个关键问题确定了端口号机制之后传输层还要回答两个问题第一多个应用共用一个网络接口数据到了之后怎么正确分发给对应的进程这叫复用与分用第二数据在传输过程中可能出错、丢失、乱序要不要检测、要不要重传、要不要保证顺序这叫服务质量的取舍。第一点是基础设施TCP 和 UDP 都做了。无论是 TCP 还是 UDP报头里都会有源端口和目标端口内核就是靠这两个字段把数据分发到对应 socket 的。第二点则是两个协议的分水岭TCP 选择了一整套复杂的可靠性机制UDP 选择把控制权交还给应用层。这里有个容易被忽略的细节传输层的数据单元到底怎么划分。TCP 是面向字节流的应用层 write 进去的数据会被 TCP 按自己的节奏切分成报文段应用层读到的是一串没有边界的字节UDP 则是面向报文的应用层每次 sendto 的数据会被封装成一个完整的 UDP 数据报接收方 recvfrom 一次取走一个报文边界清晰。1.3 为什么 TCP 和 UDP 能覆盖绝大多数需求很多初学者会奇怪网络应用千变万化为什么传输层就这两个主要协议其实原因是应用的需求可以分成两种极端要么要求数据绝对不能丢、不能乱要么要求数据必须以最低延迟送达、可以容忍少量损失。TCP 把“稳定可靠”这件事标准化了。文件传输、网页浏览、数据库交互、消息推送这些场景里丢一个字节都可能导致整个业务异常所以 TCP 成为绝对主流。UDP 则正好相反它把可靠性的责任推到应用层自己只做最基础的分发和校验换来了极低的开销和极短的响应时间适合音视频、实时游戏、DNS 这类场景。当然传输层协议不止这两个还有 SCTP、DCCP 等但它们要么场景太细分要么生态不成熟绝大部分应用根本碰不到。理解 TCP 和 UDP就等于理解了传输层 95% 的日常。2. TCP用三次握手把一个不稳定的网络变成稳定的字节流2.1 三次握手为什么必须是三次不能是两次也不能是四次TCP 建立连接的过程大家都背过客户端发 SYN服务端回 SYNACK客户端再回 ACK。但为什么要这么折腾直接发一个 SYN 然后对方回一个确认不行吗关键在于 TCP 连接要同步两个东西双方的收发能力和初始序列号。三次握手的信息量刚好能同时确认“你的发送没问题、我的接收没问题”“我的发送没问题、你的接收也没问题”。我先说为什么不能是两次。假设只握手两次客户端发 SYN服务端回 SYNACK 后就把连接资源分配好了。问题在于网络里的报文会延迟或重排如果客户端有一个很久之前发出的旧 SYN 报文被延迟到了现在才到达服务端服务端一收到就分配资源那这条连接就是一条占着资源但永远不会被使用的“死连接”。服务端等不到第三次 ACK只能靠超时把资源释放而这段时间内存和连接表都被白白占用了。三次握手中客户端收到服务端的 SYNACK 后如果发现这不是自己期望的连接根本不会回 ACK服务端也就知道这个请求已失效。再说为什么不是四次。三次握手的核心是确认双方“收发通道”都畅通第一次和第二次交换完成了对服务端能力的确认第三次 ACK 是告诉服务端“你的回复我收到了”这已经是把信息对称化所需的最少次数。四次甚至更多次握手不会带来新的语义只会增加一次额外的往返时延。连接建立已经是整个交互里最敏感的时刻了能省一个 RTT 就省一个 RTT。握手之后双方就进入 ESTABLISHED 状态。这里有个值得一提的安全话题如果攻击者不断发送伪造源地址的 SYN 报文服务端会为每个 SYN 分配半连接队列队列满了之后正常用户的握手请求就会被丢弃这就是常说的 SYN Flood。应对思路里有一招叫 SYN Cookie先不分配完整的连接资源而是把状态编码进握手响应的序列号里等第三次 ACK 回来再恢复连接有效缓解了资源耗尽问题。2.2 序列号、确认号与超时重传的配合建立连接之后TCP 要保证数据不丢、不乱。它是怎么做到的核心是给每个字节编号。TCP 是字节流协议发送方把应用层的数据切成一段一段的报文每个报文段的首字节都有一个固定的序号 seq。接收方每收到一个报文段会回一个确认号 ack表示“你下一个字节应该是多少号我期待的是这个”。比如发送方发了 seq1000、长度 500 字节的数据接收方收到后回复 ack1500意思就是前 1500 个字节我都收到了请从 1500 开始继续发。这种“累积确认”的方式有个好处即使中间有几个报文段丢了或者乱序了接收方也不着急——它只需要把自己期望的顺序号通过 ack 反馈回去。发送方如果收到三个重复的 ACK就知道某个报文段丢了会立刻重传而不必傻等超时这就是快速重传机制。如果一直没收到任何反馈TCP 会启动超时计时器估算一个合适的重传超时时间 RTO超时后重传未确认的数据。这套机制看起来简单但组合在一起解决了网络传输的核心问题不可靠的 IP 层随时可能丢包TCP 通过序号和确认号让双方始终知道“哪些数据收到了哪些还没收到”从而在应用层呈现一个连续、有序、不重复的字节流。应用层根本不需要关心底层是怎么分片的——它读到的就是一个拧干了杂质的管道。2.3 全双工与四次挥手关断比建立更讲究TCP 建立连接要三次握手断开连接却要四次挥手很多初学者不理解关闭为什么比建立还复杂因为 TCP 连接是双工的两个方向的数据流相互独立。A 向 B 发送数据和 B 向 A 发送数据本质上是两条独立的通道关闭时每一条通道都要单独“打招呼”。标准的关闭过程是这样的A 发送 FINB 收到后回复 ACK此时 A 到 B 方向的数据通道关闭但 B 可能还有数据没发完所以 B 不会立刻发 FIN等自己的数据全部发完之后B 才发 FINA 收到 B 的 FIN 后回复 ACK连接彻底关闭。这就是一进一出的四次交换中间那次 ACK 和 FIN 是分开走的所以看起来是四次。四次挥手里最值得关注的是 TIME_WAIT 状态。主动关闭方在收到对端 FIN 并发出最后一个 ACK 之后会进入 TIME_WAIT持续 2 个 MSL报文最大生存时间的时长。为什么要等这么久两个原因一是防止最后一个 ACK 在网络中丢失如果丢了对端会重发 FIN主动关闭方必须还能再回 ACK二是让这条连接上曾经发送过的所有报文在网络里彻底消失避免它们“转世”后干扰新连接。TIME_WAIT 会导致大量端口和连接表项在短时间内被占用高并发短连接的服务经常遇到这个问题。后面我会专门讲排查阶段怎么处理。3. 滑动窗口与拥塞控制TCP 不只是“可靠”它还很努力地“快”3.1 滑动窗口接收方的缓冲区放不下那么快的数据如果 TCP 每发一个包都必须等一个确认再发下一个网络利用率会非常低尤其是在高延迟链路上。为了提升效率TCP 引入了滑动窗口机制允许发送方连续发送一批数据然后一起确认。窗口大小的上限由接收方决定叫接收窗口 rwnd。接收方会在 ACK 里带上自己剩余缓冲区的大小告诉发送方“你还能再发多少字节给我”。发送方在没收到新的 ACK 之前最多只能发送一个窗口大小的数据量。这个机制叫流量控制本质是防止发送方把接收方的内存撑爆。想象一个灌水管道水龙头发送方不能不顾水池接收方缓冲区的容量一直放水否则水会溢出来。接收方用自己的剩余空间实时调整阀门传输效率和安全就平衡住了。这里有一个容易踩的坑如果接收方通告的窗口变成 0发送方就不能再发数据了双方可能僵住。TCP 的解决方案是用定时器定期发送一个探测报文看看接收方窗口恢复了没有。这个细节在排查“连接活着但数据不动”的问题时会用到。3.2 拥塞控制不是接收方慢而是路上的水管堵了流量控制解决的是“接收方吃不吃得下”的问题但网络链路本身也可能堵。如果发送方不管网络拥堵情况一股脑地灌数据路由器缓存撑不住就会大量丢包整个网络进入拥塞崩溃状态——所有人都在重传但重传的数据又加剧拥堵吞吐量反而趋近于零。TCP 的拥塞控制机制就是让发送方自己限制发送速率探测当前网络能承受的负载。它维护一个拥塞窗口 cwnd发送方的实际发送窗口取 rwnd 和 cwnd 的较小值。意思是既要照顾接收方的接收能力也要照顾中间网络的承载能力。很多人不知道的是拥塞控制并不是 TCP 协议规范里最初就有的而是在网络发生多次“拥塞崩溃”之后逐步加入的。今天我们所熟知的慢启动、拥塞避免、快速重传、快速恢复都是在真实网络事故的血泪教训中演化出来的。3.3 慢启动、拥塞避免与快速重传的协同逻辑慢启动的核心思路刚开始传输时发送方不知道网络能承载多大带宽所以从很小的窗口开始每收到一个 ACK 就把窗口翻倍1、2、4、8……指数增长。这个阶段增长很快但不至于瞬间把链路打爆因为翻倍的前提是前一批数据都成功被确认了。窗口增长到一定阈值ssthresh后TCP 进入拥塞避免阶段窗口从指数增长切换为线性增长每过一个往返时延才增加一个报文段。这个阶段非常保守是为了让链路利用率缓慢逼近极限。一旦发生超时TCP 判断网络出现拥塞做法是记录减半的 ssthresh把拥塞窗口直接降回 1重新慢启动。如果只是收到三个重复 ACK说明数据包可能丢了但网络还在运转这时不需要回到解放前而是走快速重传加快速恢复立即重传丢失的报文段拥塞窗口减半而不是归零然后继续线性增长。这些机制全部运行在操作系统协议栈内部应用层无感知但它们直接决定了你一个 TCP 连接能达到的真实吞吐量。你会发现有时候带宽明明是千兆下载速度却上不去原因往往是往返时延太长导致窗口扩展地太慢或者丢包率偏高导致一直在重传。3.4 容易被忽视的延迟 ACK 与 Nagle 算法还有两个广泛影响应用开发者的细节延迟 ACK 和 Nagle 算法。接收方通常不会立刻对每个报文段单独回 ACK而是延迟一小会儿比如 40 毫秒把多个确认合并成一个这就是延迟 ACK。它减少了网络里纯 ACK 报文的数量但也带来副作用如果应用发送模式是“写一小块数据、等一个响应、再写一小块”延迟 ACK 可能把交互时间额外拉长几十毫秒。Nagle 算法则是发送端的策略它要求发送方在“还没有收到之前所有数据的 ACK 时”只能发送一个小的数据包其余小数据要合并后再发。这个算法本意是减少小包数量但对交互式的请求-响应场景非常不友好。所以很多游戏服务器、远程终端会主动设置 TCP_NODELAY 来关闭 Nagle 算法。这里引申一个经验如果你写的 TCP 服务延迟时高时低先检查是不是“双方都开了延迟确认或 Nagle”导致的互相等待一查一个准。两个机制单独看都是合理的优化凑到一起就变成“双方都在等对方先发数据”的协作型死锁。4. UDP八字节的轻量头撑起实时网络的半边天4.1 UDP 头部拆开看小到几乎没有存在感TCP 的头部最少也要 20 字节而 UDP 的头部固定只有 8 字节源端口 2 字节、目标端口 2 字节、长度 2 字节、校验和 2 字节。对比一下TCP 头里要装序列号、确认号、标志位、窗口大小还要应对一堆状态机逻辑UDP 直接把这套东西全部省掉了。这 8 字节做的事非常有限端口保证数据能交到正确的进程长度字段说明整个 UDP 数据报的大小校验和用来检测数据在传输中是否出错出错的数据会直接被丢弃但不会触发重传。UDP 没有序列号、没有连接状态、没有流量控制、没有拥塞控制发送方拿到数据后直接封装、直接发出。它既不需要确认对端是否存在也不关心对端是否能处理得过来。对 UDP 来说通信就是“发出去了任务完成”。这种极简设计带来的直接好处是开销极小。TCP 建立连接至少要一个往返时延UDP 则是零延迟起步UDP 头部比 TCP 头部小得多也省带宽。另外UDP 天然支持广播和组播TCP 是点对点的一对一通道没法做到给一个网段内的所有机器同时发数据。4.2 无连接、无状态到底意味着什么“无连接”意味着 UDP 通信前不需要握手想发就发。这大大降低了首次数据到达的耗时对于 DNS 查询这类“一问一答”的场景特别合适——客户端发出查询请求服务器回一个响应整个过程只需要一个数据报往返不需要建立连接后的额外开销。“无状态”意味着服务端不需要维护每一条连接的信息。TCP 服务端要为每个活动连接维护发送缓冲区、接收缓冲区、序列号、窗口大小、重传定时器等一大堆状态而 UDP 服务端只做一个动作从 socket 的接收缓冲区里取数据报处理完再写数据报回去。占用的内存和 CPU 都少得多一台机器可以同时服务数量惊人的客户端。很多高性能服务选择 UDP 的原因就在这里一台服务器可以在一瞬间接收成千上万个客户端发来的报文而如果用 TCP同样的请求量可能先在三次握手上就耗尽连接表资源了。当然这也意味着 UDP 服务端很难精确判断某个客户端是不是还活着通常需要客户端定期发心跳报文来维持“在场证明”。4.3 “UDP 不可靠”这句话其实很容易被误解说 UDP 不可靠很多人以为它的数据一定容易坏、容易丢。严格来说UDP 提供了校验和能检测出传输过程中的数据破坏。问题在于它发现错误之后不会重复传输也不会通知应用层“哪一段坏了”——它只是静默地把坏报文丢掉了。所以 UDP 的“不可靠”准确含义是不保证送达、不保证顺序、不保证不重复。发送方发出两个数据报接收方可能先收到第二个再收到第一个也可能其中一个永远到不了还可能同一个报文被复制了两份。应用层如果对顺序或完整性有要求就必须自己实现确认、重传、去重和排序。相反地如果应用本身能容忍少量丢包UDP 反而是最合适的。比如实时视频通话偶尔丢一两帧画面最多产生轻微卡顿关系不大但如果为了追求“可靠”而使用 TCP网络一旦丢包TCP 会重传导致延迟骤增画面就变成长时间的停顿或卡顿体验反而更糟。这个“反而更糟”就是理解 UDP 价值的关键。4.4 为什么新协议反而往 UDP 上跑有意思的是近年很多新协议反其道而行之把底座放在 UDP 上而不是 TCP。最典型的是 HTTP/3 使用的 QUIC 协议还有实时音视频领域广泛使用的 WebRTC。先说音视频。语音、视频本身就是实时流应用层可以忍受轻微丢包却无法忍受高延迟。TCP 的重传机制在丢包时会引入额外的延迟这正好是音视频的致命伤。UDP 没有这层机制数据发出就发出延迟完全可控所以成了实时多媒体事实上的传输载体。再说 QUIC。它选择 UDP 是因为想在用户态自己实现可靠性、拥塞控制、加密和连接迁移而不是依赖操作系统内核里的 TCP 协议栈。TCP 协议栈运行在内核态升级和部署都非常麻烦很多新特性要等多年才能普及UDP 则只是提供一个简单通道所有复杂的逻辑都可以在应用层通过库实现迭代速度快得多。QUIC 还解决了 TCP 的一个老毛病队头阻塞。TCP 保证字节流有序一个包丢了后面即使先到的包也不能交给应用层QUIC 在一条连接上划分出多个独立的数据流一条流丢包不影响其他流的处理这对多路复用的场景价值极大。这些趋势说明了 UDP 的真正定位它不是弱化版的 TCP而是把可靠性策略的决定权还给了应用的程序员。你觉得需要什么级别的可靠就自己实现什么级别的可靠不必被内核里一套固定的规则绑死。5. 项目里的选型别只看“可靠不可靠”要看“能不能容忍丢包”5.1 一张表看清 TCP 和 UDP 的本质差异先给一张整理过的对照表选型和排查时拿过来一眼就能参考对比维度TCPUDP连接状态面向连接需要握手建立无连接发送前不确认对端状态可靠性可靠传输丢包重传尽力而为丢包不重传数据边界字节流无边界数据报每次发送一个完整的报文顺序保证保证到达顺序字节不乱序不保证顺序报文可能乱序到达流量控制有接收窗口限制发送速率无应用层自行控制拥塞控制有慢启动和拥塞避免无发送端不因网络拥堵降速头部开销20 字节以上固定 8 字节传输效率适合大量连续数据的可靠传输低延迟、低开销适合短小报文广播组播不支持支持典型场景网页、文件传输、数据库、邮件DNS、音视频、实时游戏、心跳上报这张表里的数据边界经常被忽略实际项目里却能坑人。TCP 的流式传输意味着如果你用 TCP 传输若干个独立的结构化消息必须在应用层自己定义消息边界比如用特殊分隔符或者前 4 个字节标记长度。而用 UDP 就不需要关心这个问题一个 sendto 对应一个 recvfrom边界天然清晰。5.2 选型决策逻辑先问自己五个问题我在项目里做传输选型时不直接看“TCP 好还是 UDP 好”而是按顺序问几个问题。第一数据丢了行不行如果一条消息丢失会导致业务流程直接失败比如订单、转账、文件持久化那基本锁死 TCP或者必须在 UDP 之上实现和应用强绑定的可靠重传复杂度会成倍增加。第二对延迟的敏感度有多高第三数据量是少量偶发还是持续流式第四服务端的并发模型能不能支撑大量长连接状态第五客户端网络环境有没有 NAT、防火墙这类限制问完之后通常落到三种结果。第一种要求高可靠且可以容忍少量额外延迟直接 TCP。第二种实时性优先且能容忍轻微丢失用 UDP并在应用层根据需要决定是否加简单重传。第三种也是相当常见的方案核心控制通道用 TCP 保证可靠实时数据通道用 UDP 保证低延迟两条通道同时跑。举个例子我维护过的一个模拟项目 X 里做物联网设备接入设备定时上报状态每条报文只有几十字节量却很大。如果用 TCP设备需要维护长连接断线重连、心跳保活、半开连接检测一大堆问题而直接用 UDP 上报服务端无状态、可水平扩展丢了一两条旧状态也无所谓下一条马上就到。但同一套系统里下发设备升级指令的命令通道必须要可靠这类消息一旦丢了会造成设备版本不一致所以走了 TCP。这就是典型的“控制走 TCP、数据走 UDP”分工。5.3 防火墙和 NAT 带来的现实差异选完协议只是第一步真正部署时NAT 和防火墙往往比协议本身更让人头疼。TCP 是有状态的协议防火墙会记录连接状态内网客户端主动访问外网后回来的流量能顺利通过UDP 无状态很多防火墙对 UDP 的策略简单粗暴要么是“开端口放行所有 UDP”要么是“默认丢弃”。内网穿透领域常说的“UDP 打洞”利用的正是 UDP 的无状态特性。两个终端都在 NAT 后面双方通过中间服务器交换彼此的地址信息然后互相直接发 UDP 包只要 NAT 记住了这条映射关系两边的数据就能通。TCP 打洞的难度更高因为连接建立涉及更多的状态同步。这也是很多实时通信软件优先选择 UDP 的底气。另一个现实差异是连接保活。TCP 虽然本身有 KeepAlive 机制但操作系统默认要等两小时才开始第一次探测对大多数应用来说形同虚设最终还是得应用层自己维护心跳。UDP 没有连接概念一头不通另一头完全无感所以用 UDP 的应用必须主动设计心跳机制否则无法判断对端是否已经离线。可以说UDP 把“连接管理”这件事整个踢回给了应用程序这也是它“简单但绝不省心”的一面。6. 观察和排查怎么从连接状态里看出网络问题的门道6.1 用几个命令看懂一台机器的 TCP 连接状态很多开发写 TCP 程序写了几年也没认真看过连接状态。实际上用一条命令就能看到非常丰富的信息netstat -an | head -20或者更现代的ss -s ss -antss -s会输出整个系统的 TCP socket 统计包括已建立连接数、等待关闭数、各状态计数ss -ant可以列出所有连接的状态和地址。看到一堆状态时先记住几个最常见的LISTEN 表示端口正在监听ESTABLISHED 表示连接已经建立TIME_WAIT 表示主动关闭方正在等待超时CLOSE_WAIT 表示被动关闭方还没有关掉自己的 socket。每个状态背后都有意义。比如一台服务器上出现大量 TIME_WAIT通常说明它的某个服务在频繁地主动关闭连接。网上很多文章把它描述成“洪水猛兽”其实在短连接请求多的场景下这是完全正常的TCP 主动关闭方就是要承担这个等待成本。真正要注意的不是 TIME_WAIT 多不多而是有没有耗尽本地端口每个 TCP 连接四元组里客户端端口只有 65535 减到保留端口那么多如果连接全部挤在 TIME_WAIT 状态占用端口新连接就可能因为端口不足而失败。优化手段也比较成熟比如调整 TIME_WAIT 重用、缩短 MSL 时间但前提是确认业务没有依赖 TIME_WAIT 提供的延迟报文消除能力。6.2 两个高频故障CLOSE_WAIT 堆积和 RST 报文相比 TIME_WAIT 的正常性CLOSE_WAIT 堆积基本可以断定是应用层代码的问题。当对端发送 FIN 请求关闭连接时本端 TCP 协议栈会先进入 CLOSE_WAIT 状态然后由操作系统通知应用层“对方已关闭”应用层应该在处理完数据之后调用 close() 关闭自己的 socket连接才会进入 LAST_ACK 并最终断开。如果应用程序不调用 close()这个 socket 就永远停留在 CLOSE_WAIT文件描述符、内存和端口全部被占住。出现大量 CLOSE_WAIT几乎可以断定是应用层某个读事件没有正确触发 close 分支或者资源被线程池卡死了。另一种常见现象是 RST。通信一方如果觉得这条连接已经不正常就会发一个 RST 把连接强制断掉。RST 常见来源有三种往一个没有进程监听的端口发数据目标会立刻回 RST这在端口探测时经常看到防火墙或中间设备干预直接重置连接对端应用程序主动设置 SO_LINGER 并关闭未处理的数据也会产生 RST。排查 RST 时先用抓包工具看一下 RST 是从哪一侧发出的往往就能缩小到目标机器上的具体进程。6.3 UDP 丢包排查先分链路再看缓冲区最后查包大小UDP 没有重传机制丢包只能靠应用层和探测手段来发现。排查顺序有讲究。第一步看链路层和网络层的丢包状况。在 Linux 上可以用netstat -su查看 UDP 协议统计重点看 receive buffer errors这个数字如果持续增长说明内核接收缓冲区满了数据报被直接丢弃。遇到这种情况要改/proc/sys/net/core/rmem_max和 socket 的接收缓冲区大小同时检查应用是不是来不及处理数据。第二步关注 MTU。UDP 加 IP 头后的总长度如果超过链路 MTUIP 层就要分片转发分片报文一旦在途中丢失整个数据报就重组失败。在公网上大量 UDP 报文不翼而飞时先把 UDP 载荷控制在 1400 字节以内能避开大多数分片坑。第三步检查 ICMP 不可达消息。UDP 端口没有服务在监听时对端会回一个 ICMP Port Unreachable。但很多公网环境会限制 ICMP导致发送方根本感知不到这种情况数据发出去了而接收方根本没人收。所以用 UDP 做服务时服务端的状态监控绝不能省。排查看上去内容很多但我总结出一条经验TCP 的问题绝大多数出在连接状态和防火墙策略上UDP 的问题绝大多数出在缓冲区、报文大小和 ICMP 被过滤上。按这个方向定位成功率很高。写在最后的一个实操体会最后分享一点我自己长期调网络问题的体会。TCP 和 UDP 从来不是对立关系它们在传输层各司其职TCP 把复杂隐藏在协议栈内部给应用一个简单可靠的字节流管道UDP 把复杂性明明白白摆在开发者面前用简单保住低延迟的底线。每次做传输选型时我会先问团队一句我们更怕丢数据还是更怕慢如果答案是怕丢不要犹豫用 TCP 把所有可靠性问题交给内核如果答案是怕慢且能容忍局部丢失那么 UDP 加应用层定制策略才是正路。还有一条务实建议新功能第一版如果没有明确的低延迟诉求优先用 TCP 把业务逻辑跑通等明确瓶颈真的出在延迟上再考虑迁到 UDP而不是一开始就跟自己过不去。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑