解构云游戏协议:从 RTSP 握手到 NALU 碎片化

深入云游戏的心脏,解析 HTTP/RTSP/RTP 协议栈的分工,H.264 码流的原子结构,以及为何在输入传输中选择了 Reliable UDP。

云游戏的核心不在于渲染(那是服务端显卡的事),而在于如何把渲染好的画面低延迟地推送到客户端,并把客户端的操作精准地送回服务端

这篇文章将带你解构这一整套复杂的协议栈。

1. 协议栈全景:各司其职的精密机器

云游戏的通信过程可以分为三个明确的阶段,每个阶段使用不同的专有协议:

第一阶段:握手与发现 (HTTP/XML)

就像网购前先浏览商品,客户端首先需要知道服务端有哪些游戏可玩。

  • 协议:HTTP
  • 内容:客户端请求 /serverinfo 获取服务端状态,请求 /applist 获取游戏列表。
  • 数据格式:XML(是的,NVIDIA GameStream 协议还在使用古老的 XML)。
  • 职责:非实时业务逻辑,配对认证。

第二阶段:谈判与控制 (RTSP)

选好游戏准备启动时,并不直接传视频,而是先派出一个谈判专家 —— RTSP (Real Time Streaming Protocol)

  • 类比:电视遥控器。
  • 职责控制平面 (Control Plane)
    • 协商 (Negotiation):客户端告诉服务端:“我要 1080P,60FPS,HEVC 编码,环绕声”。服务端回应:“好的,支持”。
    • 控制 (Control):发送 SETUP, PLAY, PAUSE, TEARDOWN 指令。
  • 特点:TCP 连接,要求可靠,允许低频互动。

第三阶段:数据狂流 (RTP)

谈判结束,电影开场。海量的音视频数据倾泻而下。

  • 类比:快递包裹流。
  • 协议RTP (Real-time Transport Protocol) over UDP。
  • 职责数据平面 (Data Plane)。负责高频传输 H.264/H.265 视频帧和 PCM/Opus 音频帧。
  • 特点:UDP 传输,追求极低延迟,容忍少量丢包(画面花一下总比卡住强)。

2. NALU:视频流的原子解剖

在处理 RTP 包时,我们必须理解视频流的最小单元 —— NALU (Network Abstraction Layer Unit)。直接把 H.264 文件扔进 socket 是不行的,必须进行"外科手术式"的拆解。

关键角色

  1. SPS (Sequence Parameter Set) & PPS (Picture Parameter Set)

    • 比喻:书籍的目录和版权页
    • 作用:告诉解码器视频的分辨率、Profile、Level 等元数据。
    • 死穴:如果解码器没先收到 SPS/PPS 就收到了视频帧,它将一脸懵逼,完全无法解码。因此,建立连接首要任务就是确保这两个 NALU 准确送达。
  2. IDR 帧 (Instantaneous Decoding Refresh)

    • 比喻全彩插图
    • 作用:也就是I帧(关键帧)。它不依赖任何之前的画,包含完整的图像信息。
    • 填坑:当网络波动导致画面大面积花屏(P帧参考丢失)时,客户端必须立即通过控制通道请求一个 IDR 帧,强制刷新画面,这叫 FIR (Full Intra Request)
  3. P 帧 (Predictive)

    • 比喻透明贴纸
    • 作用:只记录相对于前一帧的变化(残差)。数据量极小,是云游戏带宽节省的主力。

碎片化 (Fragmentation)

一个 4K 的 I 帧可能高达几百 KB。而以太网的 MTU (最大传输单元) 通常只有 1500 字节(UDP 有效载荷通常限制在 1400 左右)。

必须切片!

我们需要手动实现 H.264 Fragmentation Unit (FU-A) 拆包逻辑:

  1. 检测 NALU 大小。
  2. 如果 > MTU,切分为多个包。
  3. 第一个包标记 Start,最后一个标记 End
  4. 接收端收到后,必须把这些碎片重新拼成一个完整的 NALU,加上 Start Code (00 00 00 0100 00 01),也就是"章节装订线",解码器才能识别。

3. 输入传输:为何选择 Reliable UDP?

画面的传输解决了,那操作呢?我的鼠标移动、键盘按下,该用什么传?

TCP 的死穴:队头阻塞 (Head-of-Line Blocking)

如果用 TCP,按下 “W” (Packet 1) 和 “Click” (Packet 2)。如果 Packet 1 丢了,TCP 协议栈会暂停 Packet 2 的递交,死等 Packet 1 重传成功。 在游戏里,这意味着你的操作卡顿。对于 FPS 游戏,“现在"的操作比"以前"的操作重要得多,卡顿是致命的。

纯 UDP 的风险

如果用纯 UDP,“开火"指令丢了,那就真的丢了。你按了鼠标,枪没响,被反杀。不可接受。

完美折中:ENet (Reliable UDP)

我们选择了 ENet(或者 KCP、QUIC 等类似技术)。它是构建在 UDP 之上的应用层协议,提供了灵活的可靠性机制:

  1. 可选可靠性

    • 鼠标移动:设置为 Unreliable。满屏幕乱飞的鼠标轨迹,丢几个点无所谓,最新的点到了就行。
    • 按键/点击:设置为 Reliable。必须送达,底层会自动重传,确保逻辑正确。
  2. 多通道 (Channels)

    • ENet 允许定义多个 Channel。
    • Channel 0 (Input): 传输按键。
    • Channel 1 (Haptics): 传输手柄震动。
    • 优势:震动数据的丢包重传不会阻塞按键数据的发送。各通道独立,互不干扰。

总结

云游戏协议的设计,就是在实时性(UDP)和可靠性(TCP/Reliable UDP)之间走钢丝。

  • 视频流:宁愿花屏,不能卡顿 -> RTP over UDP
  • 控制流:必须准确,延迟不敏感 -> RTSP over TCP
  • 输入流:既要准确(按键),又要实时(鼠标),还要防阻塞 -> ENet (Reliable UDP)

理解了这些,才算真正摸到了云游戏的脉搏。