<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ENet on zed的博客</title><link>https://www.dust-zed.site/tags/enet/</link><description>Recent content in ENet on zed的博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 02 Jan 2026 22:00:00 +0800</lastBuildDate><atom:link href="https://www.dust-zed.site/tags/enet/index.xml" rel="self" type="application/rss+xml"/><item><title>解构云游戏协议：从 RTSP 握手到 NALU 碎片化</title><link>https://www.dust-zed.site/computer-science/network/streaming-protocols-deep-dive/</link><pubDate>Fri, 02 Jan 2026 22:00:00 +0800</pubDate><guid>https://www.dust-zed.site/computer-science/network/streaming-protocols-deep-dive/</guid><description>&lt;p>云游戏的核心不在于渲染（那是服务端显卡的事），而在于&lt;strong>如何把渲染好的画面低延迟地推送到客户端，并把客户端的操作精准地送回服务端&lt;/strong>。&lt;/p>
&lt;p>这篇文章将带你解构这一整套复杂的协议栈。&lt;/p>
&lt;h2 id="1-协议栈全景各司其职的精密机器">1. 协议栈全景：各司其职的精密机器
&lt;/h2>&lt;p>云游戏的通信过程可以分为三个明确的阶段，每个阶段使用不同的专有协议：&lt;/p>
&lt;h3 id="第一阶段握手与发现-httpxml">第一阶段：握手与发现 (HTTP/XML)
&lt;/h3>&lt;p>就像网购前先浏览商品，客户端首先需要知道服务端有哪些游戏可玩。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>协议&lt;/strong>：HTTP&lt;/li>
&lt;li>&lt;strong>内容&lt;/strong>：客户端请求 &lt;code>/serverinfo&lt;/code> 获取服务端状态，请求 &lt;code>/applist&lt;/code> 获取游戏列表。&lt;/li>
&lt;li>&lt;strong>数据格式&lt;/strong>：XML（是的，NVIDIA GameStream 协议还在使用古老的 XML）。&lt;/li>
&lt;li>&lt;strong>职责&lt;/strong>：非实时业务逻辑，配对认证。&lt;/li>
&lt;/ul>
&lt;h3 id="第二阶段谈判与控制-rtsp">第二阶段：谈判与控制 (RTSP)
&lt;/h3>&lt;p>选好游戏准备启动时，并不直接传视频，而是先派出一个谈判专家 —— &lt;strong>RTSP (Real Time Streaming Protocol)&lt;/strong>。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>类比&lt;/strong>：电视遥控器。&lt;/li>
&lt;li>&lt;strong>职责&lt;/strong>：&lt;strong>控制平面 (Control Plane)&lt;/strong>。
&lt;ul>
&lt;li>&lt;strong>协商 (Negotiation)&lt;/strong>：客户端告诉服务端：&amp;ldquo;我要 1080P，60FPS，HEVC 编码，环绕声&amp;rdquo;。服务端回应：&amp;ldquo;好的，支持&amp;rdquo;。&lt;/li>
&lt;li>&lt;strong>控制 (Control)&lt;/strong>：发送 &lt;code>SETUP&lt;/code>, &lt;code>PLAY&lt;/code>, &lt;code>PAUSE&lt;/code>, &lt;code>TEARDOWN&lt;/code> 指令。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>特点&lt;/strong>：TCP 连接，要求可靠，允许低频互动。&lt;/li>
&lt;/ul>
&lt;h3 id="第三阶段数据狂流-rtp">第三阶段：数据狂流 (RTP)
&lt;/h3>&lt;p>谈判结束，电影开场。海量的音视频数据倾泻而下。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>类比&lt;/strong>：快递包裹流。&lt;/li>
&lt;li>&lt;strong>协议&lt;/strong>：&lt;strong>RTP (Real-time Transport Protocol)&lt;/strong> over UDP。&lt;/li>
&lt;li>&lt;strong>职责&lt;/strong>：&lt;strong>数据平面 (Data Plane)&lt;/strong>。负责高频传输 H.264/H.265 视频帧和 PCM/Opus 音频帧。&lt;/li>
&lt;li>&lt;strong>特点&lt;/strong>：UDP 传输，追求极低延迟，容忍少量丢包（画面花一下总比卡住强）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="2-nalu视频流的原子解剖">2. NALU：视频流的原子解剖
&lt;/h2>&lt;p>在处理 RTP 包时，我们必须理解视频流的最小单元 —— &lt;strong>NALU (Network Abstraction Layer Unit)&lt;/strong>。直接把 H.264 文件扔进 socket 是不行的，必须进行&amp;quot;外科手术式&amp;quot;的拆解。&lt;/p>
&lt;h3 id="关键角色">关键角色
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>SPS (Sequence Parameter Set) &amp;amp; PPS (Picture Parameter Set)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>比喻&lt;/strong>：书籍的&lt;strong>目录和版权页&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>作用&lt;/strong>：告诉解码器视频的分辨率、Profile、Level 等元数据。&lt;/li>
&lt;li>&lt;strong>死穴&lt;/strong>：如果解码器没先收到 SPS/PPS 就收到了视频帧，它将一脸懵逼，完全无法解码。因此，建立连接首要任务就是确保这两个 NALU 准确送达。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>IDR 帧 (Instantaneous Decoding Refresh)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>比喻&lt;/strong>：&lt;strong>全彩插图&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>作用&lt;/strong>：也就是I帧（关键帧）。它不依赖任何之前的画，包含完整的图像信息。&lt;/li>
&lt;li>&lt;strong>填坑&lt;/strong>：当网络波动导致画面大面积花屏（P帧参考丢失）时，客户端必须立即通过控制通道请求一个 IDR 帧，强制刷新画面，这叫 &lt;strong>FIR (Full Intra Request)&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>P 帧 (Predictive)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>比喻&lt;/strong>：&lt;strong>透明贴纸&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>作用&lt;/strong>：只记录相对于前一帧的变化（残差）。数据量极小，是云游戏带宽节省的主力。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="碎片化-fragmentation">碎片化 (Fragmentation)
&lt;/h3>&lt;p>一个 4K 的 I 帧可能高达几百 KB。而以太网的 MTU (最大传输单元) 通常只有 1500 字节（UDP 有效载荷通常限制在 1400 左右）。&lt;/p>
&lt;p>&lt;strong>必须切片！&lt;/strong>&lt;/p>
&lt;p>我们需要手动实现 &lt;strong>H.264 Fragmentation Unit (FU-A)&lt;/strong> 拆包逻辑：&lt;/p>
&lt;ol>
&lt;li>检测 NALU 大小。&lt;/li>
&lt;li>如果 &amp;gt; MTU，切分为多个包。&lt;/li>
&lt;li>第一个包标记 &lt;code>Start&lt;/code>，最后一个标记 &lt;code>End&lt;/code>。&lt;/li>
&lt;li>接收端收到后，必须把这些碎片重新拼成一个完整的 NALU，加上 &lt;strong>Start Code&lt;/strong> (&lt;code>00 00 00 01&lt;/code> 或 &lt;code>00 00 01&lt;/code>)，也就是&amp;quot;章节装订线&amp;quot;，解码器才能识别。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-输入传输为何选择-reliable-udp">3. 输入传输：为何选择 Reliable UDP？
&lt;/h2>&lt;p>画面的传输解决了，那操作呢？我的鼠标移动、键盘按下，该用什么传？&lt;/p>
&lt;h3 id="tcp-的死穴队头阻塞-head-of-line-blocking">TCP 的死穴：队头阻塞 (Head-of-Line Blocking)
&lt;/h3>&lt;p>如果用 TCP，按下 &amp;ldquo;W&amp;rdquo; (Packet 1) 和 &amp;ldquo;Click&amp;rdquo; (Packet 2)。如果 Packet 1 丢了，TCP 协议栈会暂停 Packet 2 的递交，死等 Packet 1 重传成功。
在游戏里，这意味着你的&lt;strong>操作卡顿&lt;/strong>。对于 FPS 游戏，&amp;ldquo;现在&amp;quot;的操作比&amp;quot;以前&amp;quot;的操作重要得多，卡顿是致命的。&lt;/p>
&lt;h3 id="纯-udp-的风险">纯 UDP 的风险
&lt;/h3>&lt;p>如果用纯 UDP，&amp;ldquo;开火&amp;quot;指令丢了，那就真的丢了。你按了鼠标，枪没响，被反杀。不可接受。&lt;/p>
&lt;h3 id="完美折中enet-reliable-udp">完美折中：ENet (Reliable UDP)
&lt;/h3>&lt;p>我们选择了 &lt;strong>ENet&lt;/strong>（或者 KCP、QUIC 等类似技术）。它是构建在 UDP 之上的应用层协议，提供了灵活的可靠性机制：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>可选可靠性&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>鼠标移动&lt;/strong>：设置为 &lt;strong>Unreliable&lt;/strong>。满屏幕乱飞的鼠标轨迹，丢几个点无所谓，最新的点到了就行。&lt;/li>
&lt;li>&lt;strong>按键/点击&lt;/strong>：设置为 &lt;strong>Reliable&lt;/strong>。必须送达，底层会自动重传，确保逻辑正确。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>多通道 (Channels)&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>ENet 允许定义多个 Channel。&lt;/li>
&lt;li>&lt;strong>Channel 0 (Input)&lt;/strong>: 传输按键。&lt;/li>
&lt;li>&lt;strong>Channel 1 (Haptics)&lt;/strong>: 传输手柄震动。&lt;/li>
&lt;li>&lt;strong>优势&lt;/strong>：震动数据的丢包重传&lt;strong>不会阻塞&lt;/strong>按键数据的发送。各通道独立，互不干扰。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>云游戏协议的设计，就是在&lt;strong>实时性&lt;/strong>（UDP）和&lt;strong>可靠性&lt;/strong>（TCP/Reliable UDP）之间走钢丝。&lt;/p>
&lt;ul>
&lt;li>视频流：宁愿花屏，不能卡顿 -&amp;gt; &lt;strong>RTP over UDP&lt;/strong>。&lt;/li>
&lt;li>控制流：必须准确，延迟不敏感 -&amp;gt; &lt;strong>RTSP over TCP&lt;/strong>。&lt;/li>
&lt;li>输入流：既要准确（按键），又要实时（鼠标），还要防阻塞 -&amp;gt; &lt;strong>ENet (Reliable UDP)&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>理解了这些，才算真正摸到了云游戏的脉搏。&lt;/p></description></item></channel></rss>