假设我们正在做一个在线白板:打开页面后,两个人可以一起画图、移动光标、语音交流,还能上传文件。画完的内容需要保存到服务器。
这些数据都经过网络,但它们对网络的要求并不相同。
上传文件时,少一个字节都不行,慢一点通常可以接受。移动光标时,新的位置已经到了,半秒前的位置反而没有必要补回来。语音也有时效性,但丢掉一段声音后,还需要决定怎样维持连续播放。保存白板时,即使所有请求字节都成功发送,也不代表服务器已经把修改写进数据库。
因此,“选一个更快的协议”还不是一个足够明确的问题。我们要先问:哪些数据必须完整?哪些数据过时就可以丢弃?收到什么,才算事情完成?
本文沿着这三个问题展开:先从 TCP 的可靠传输走到 QUIC 和 HTTP/3,再解释实时通信为什么需要 WebRTC、WebTransport 这样的接口,最后讨论 gRPC、MQTT、DDS 和 CoAP 如何表达应用真正关心的操作与消息。阅读时只需要知道:网络把数据拆成包发送,包可能丢失、重复、乱序,往返一次需要一定时间。
先分清:这些名字并不处于同一层
一次“保存白板”的远程调用,可以通过 gRPC 表达,通过 HTTP/2 承载,再由 TCP 传输。它们不是三个相互替代的方案,而是在回答不同的问题。
| 要解决的问题 | 代表机制或协议 | 尚未替应用解决的事 |
|---|---|---|
| 如何在网络上交付字节或数据报 | TCP、UDP、QUIC | 字节代表什么业务操作 |
| 如何表达请求、响应和浏览器连接 | HTTP/2、HTTP/3、WebSocket、WebTransport | 重试是否会重复修改业务状态 |
| 如何建立并维持实时会话 | WebRTC 协议栈 | 应用自己的用户、房间与权限逻辑 |
| 如何调用远端方法或分发消息 | gRPC、MQTT、DDS、CoAP | 所有外部副作用是否已经完成 |
这里的分组是为了区分职责,不是严格的 OSI 层次表。WebRTC 本身是一组协作协议;WebTransport 同时涉及浏览器 API 和线上传输映射;DDS 也不仅仅是一个报文格式。
先把这些边界分开,后面才能看清:新协议究竟修正了哪个旧问题,又把哪些问题留给了应用。
从丢包开始:可靠传输需要付出什么
先保证正确,再考虑速度
白板要上传一个文件。最简单的办法是发出一块数据,然后等接收端回复“收到了”,再发下一块。
但如果没有等到回复,发送端不能立即断定数据丢了:数据可能没到,也可能已经到达,只是确认丢了。于是我们需要给数据编号,让接收端能识别重复内容;还需要超时或其他丢包判断机制,在必要时补发。接收端再根据编号整理顺序,才能向应用交付完整内容。TCP 将这些工作组织成可靠、有序的 byte stream;一次应用写入并不对应接收端的一次读取,应用还要自己定义消息边界。(Kao, n.d.)
这里的“可靠”不是承诺网络永远可用。连接最终仍可能失败;协议做的是检测缺口、尝试恢复,并在成功的连接上提供约定的交付语义。
等一个确认再发下一块虽然容易理解,却可能浪费绝大部分链路容量。假设:
- 每块有效数据为
bytes; - 往返时间
; - 瓶颈链路速率
。
暂时忽略头部和发送这一小块数据本身的时间,每轮只能交付
链路每秒本来可以传约
浪费发生在等待期间:前一块数据还在路上,后面的数据已经可以出发。要让链路在一次往返中持续工作,就需要同时容纳大约“一次往返期间能够发出去的数据量”:
这就是 bandwidth-delay product 在这个简化场景中的含义。窗口允许多个尚未确认的数据块同时在途,而不是把每次发送都变成一次完整的来回等待。它给出的是量级直觉,不是实际连接只要设置成
不能只问“还能发多少”
允许更多数据在途后,又出现两个不同的限制。
接收端可能来不及处理数据,自己的缓冲区快满了。它需要通过 flow control 告诉发送端还有多少接收空间。(Kao, n.d.) 即使接收端内存很大,中间链路也可能已经拥堵;发送端还需要通过 congestion control 调整进入网络的数据量。(Kao, n.d.a) 前者保护接收端,后者保护路径。
如果在已经饱和的瓶颈前多堆
的排队等待。更多缓冲并没有让链路变快,只是让数据更晚到达。
这也解释了为什么实时通信不能简单地把拥塞控制关掉:发送得越积极,可能越是在替未来的语音积累延迟。
HTTP/2 到 QUIC:不要让无关的请求一起等待
打开白板页面时,浏览器可能同时请求样式、脚本和缩略图。它们不必全部依次完成。
HTTP/2 可以把不同请求的内容切成 frame,用 stream ID 区分,再交错放进同一条连接。例如,先发送脚本的一部分,再发送缩略图的一部分。应用层因此可以并发处理多个请求,不再要求把一个响应全部发完后才开始下一个。(Thomson & Benfield, 2022)
问题是:HTTP/2 通常把这些 frame 放进同一条 TCP byte stream。假设顺序是
TCP 并不知道
QUIC 把可靠性放回每条 stream
要解除这种牵连,光在 TCP 上再加一个 stream ID 不够:TCP 的交付接口已经要求所有字节按序出现。我们需要让传输层直接认识多条 stream。
QUIC 让每条 stream 有自己的字节偏移和交付顺序。收到
这种独立性也有边界。不同 stream 仍共享连接级拥塞控制;一个 QUIC packet 也可以携带多条 stream 的数据,丢掉它可能同时影响多条 stream。某条 stream 内部的缺口,仍然会阻塞这条 stream 后续字节的有序交付。(Iyengar & Swett, 2021; Iyengar & Thomson, 2021)
QUIC 使用 UDP 承载,但可靠性不是 UDP 提供的。确认、丢包检测、重传、流量控制和拥塞控制都由 QUIC 自己实现。使用 UDP 的一个工程价值,是让新的传输逻辑可以随端点软件演进,而不必首先把一种新的传输层协议部署到整条互联网路径。它并不意味着 QUIC 不受网络限制:UDP 被阻断时,HTTP 客户端仍需要尝试基于 TCP 的 HTTP。(Bishop, 2022; ETH Networked Systems Group, 2025)
为什么 QUIC 还要把 packet number 和 stream offset 分开?
假设 packet 17 携带 stream A 的第
因此,ACK 确认 packet 23 时,发送端能知道被确认的是后一次发送,而不是含糊地猜测原包还是重传包到达了。QUIC 的恢复过程重新发送仍然需要的信息,不要求原封不动重发整个旧包。packet number 在各自的编号空间内不复用;stream offset 则继续描述原来的逻辑字节位置。(Iyengar & Swett, 2021; Iyengar & Thomson, 2021)
少一次等待,还要处理握手和移动网络
传数据之前,双方通常还要确认协议参数并建立加密密钥。QUIC 把 TLS 1.3 握手整合进连接建立过程;在普通、无额外 Retry 的首次握手中,客户端可在一次往返后开始发送应用数据。曾经连接过的客户端,在满足恢复条件时还可以尝试 0-RTT,把早期数据放进首次发出的数据包。(Thomson & Turner, 2021)
但 0-RTT 不是“所有连接都没有握手开销”。它依赖此前获得的状态,服务端也可以拒绝早期数据;更重要的是,早期数据存在 replay 风险。把“查询白板列表”和“新建一次扣费操作”同样放进可重放请求里,后果显然不同。能否使用 0-RTT,需要应用参与判断。(Thomson & Turner, 2021)
移动设备还有另一个问题:从 Wi-Fi 切换到移动网络后,IP 地址和端口可能变化。QUIC 用 connection ID 帮助识别连接,使连接不必只绑定在原来的地址组合上。不过,新路径仍需验证,端点也必须保留连接状态并允许相应迁移;connection ID 并不保证任意断网都能无缝恢复。(Iyengar & Thomson, 2021)
HTTP/3 改变承载方式,不改变“请求一个资源”
有了 QUIC,HTTP/3 就可以把一次请求和响应放到一条双向 QUIC stream 上。HTTP 的方法、状态码和资源语义没有因此变成另一套东西,改变的是这些语义如何在线上传输。(Bishop, 2022)
“QUIC 已经独立分流”也不等于 HTTP/3 完全没有等待。为了压缩 header,QPACK 可以引用动态表中的条目;如果解码某个 header 所需的表更新还没到,它仍可能被阻塞。这是压缩状态的依赖,不是 TCP 字节流的缺口。(Bishop, 2022)
所以,更准确的结论是:QUIC 拆开了一类原本不必要的交付依赖,而不是消灭了一切依赖,也不是保证任何网络、任何负载下都比 TCP 更快。
实时通信:正确的数据也可能已经失去价值
文件下载的自然目标是最终拿到完整文件。但在白板语音里,假设一句话中的一个音频包迟到了两秒:它的字节即使完全正确,也通常不应该插回当前正在播放的句子。
这里出现了与可靠性不同的维度:数据什么时候失效?
先看一个具体例子。每个音频包包含
| 包 | 帧首样本产生 | 开始发送 | 网络耗时 | 到达 |
|---|---|---|---|---|
| 1 | 0 | 25 | 32 | 57 |
| 2 | 20 | 45 | 38 | 83 |
| 3 | 40 | 65 | 29 | 94 |
| 4 | 60 | 85 | 61 | 146 |
| 5 | 80 | 105 | 34 | 139 |
第 5 包甚至比第 4 包先到。如果收到一个包就立刻播放,声音会忽快忽慢,还可能乱序。接收端需要按照媒体时间安排播放,并用 jitter buffer 吸收一部分到达波动。RTP 为媒体提供序号和时间戳:序号帮助识别丢失与乱序,时间戳表达媒体采样时间,而不是简单记录这个包的发送墙钟。(Schulzrinne et al., 2003)
多等一点,究竟换来了什么
我们给每个包设定相同的预算
包必须在这个时刻之前到达。把
如果
这里容易混淆“预算”和“实际缓冲时间”。一个按时到达的包,在接收端真正等待的是
网络花得越久,留给缓冲区的等待就越少。假设解码和播放还要
也就是
来得及的时候,重传仍然有用
“实时”并不意味着所有包都禁止重传。如果离播放还有足够时间,补回一个丢包可能很值得;来不及重传时,可以利用此前发送、且已及时到达的纠错冗余恢复内容,或者由解码器估计缺失的声音。这分别是前向纠错(FEC)和丢包隐藏的思路。冗余需要提前发送,会消耗额外带宽,也可能增加等待;这些办法都无效时,就只能放弃过时内容。(Perkins et al., 2021)
视频还存在帧间依赖,丢掉一个重要参考帧可能影响后续多帧,因此不能仅凭“这个包比较旧”决定它没有价值。编码依赖可以结合多媒体压缩中的视频部分理解。
缓冲也只能吸收短期波动,不能解决持续超载。假设编码器每秒产生
WebRTC:实时会话不仅仅是一条 UDP 连接
即使已经理解媒体时限,两个浏览器也不能直接开始通话。它们还要知道对方支持什么格式、去哪里发送数据,以及如何建立安全连接。
先找到彼此,再确认路径真的可达
白板应用首先通过自己的服务交换用 SDP 表达的会话描述和相关连接信息,例如支持的媒体格式、ICE 凭据和证书指纹。这个过程叫 signaling,可以使用 HTTPS 或 WebSocket 等机制;WebRTC 不替应用规定用户怎样登录、怎样进入房间,也不规定必须使用哪一种 signaling 协议。(Alvestrand, 2021)
接下来,地址本身还可能不够。家庭路由器等设备常通过 NAT 在私网和外网之间转换地址,浏览器看到的私网地址不一定能被对方访问。因此,需要收集和验证几种可能的联系路径:
- 本地地址形成 host candidate。
- STUN 可以让端点得知服务器观察到的映射地址,形成 server-reflexive candidate。
- TURN 可以分配 relay candidate,让数据经中继转发。
这些 candidate 只是可能可达的地址,不是可达性的保证。ICE 将两端候选配对并执行连接检查,再通过提名过程选用有效路径;不是拿到一个公网地址就自动打通所有 NAT。(Keranen et al., 2018)
TURN 也不是失败后才神奇出现的一条必通隧道:中继服务需要部署、鉴权和带宽,网络策略仍可能阻断连接。WebRTC 的传输要求还考虑了 UDP 不可用时的 TCP/TLS 中继路径,因此“WebRTC 永远只走 UDP”并不准确。(Alvestrand, 2021b)
路径、加密与媒体分别承担什么
路径建立后,双方通过 DTLS 握手建立密钥材料。握手中的证书还要与会话描述中的指纹匹配;因此,身份保证也依赖 signaling 或其他身份验证机制对指纹的可信绑定。(Rescorla, 2021) WebRTC 媒体必须由 SRTP/SRTCP 保护;DataChannel 则使用 SCTP over DTLS。这里不是把所有 RTP 包都塞进 DTLS 的应用数据记录中,而是使用 DTLS 导出的密钥保护媒体包。(Alvestrand, 2021b)
RTP 承担媒体序号和时间戳等信息,RTCP 提供相关反馈;真正把声音连续播出来,还要依靠编解码、缓冲、丢包恢复和拥塞适应。WebRTC 的价值正在于把这些机制组织成能够互通的实时通信体系,而不只是提供一个发送 UDP 的 API。(Perkins et al., 2021)
多人会议还常使用 SFU:每个参与者把媒体发给服务器,由服务器选择性转发给其他参与者。这与 TURN 的通用中继角色不同。SFU 即使只转发而不解码视频,也不代表它默认无法解密媒体;如果希望媒体内容对转发服务器保持不可见,需要额外的端到端内容保护,例如 SFrame,并确保 SFU 不持有相应内容密钥。密钥分发与成员权限仍需要应用设计,转发所需的部分元数据也不因此隐藏。(Omara et al., 2024)
DataChannel:可靠和有序是两个旋钮
语音之外,白板还要传光标和绘图数据。WebRTC DataChannel 提供有消息边界的数据传输,并允许配置顺序和可靠性。可靠、有序适合必须完整交付的内容;无序、限制重传的配置则适合某些时效性更新。无序不等于不可靠:是否等待先前消息,与是否努力补回丢失消息,是两个问题。(Jesup et al., 2021)
但应用的数据表示必须配合这种选择。
若每条光标消息都是“当前位置为
同理,白板上的持久笔画不能因为“也是实时数据”就随便丢弃。它可以使用可靠传输,也可以由应用设计版本、确认和补同步机制;可靠性必须在某一层真正落实。
WebTransport:对端是服务器时,还需要什么
如果光标、游戏状态或协作数据本来就要经过服务器,那么应用未必需要整套对等媒体会话机制。它可能只想在同一个会话里同时拥有可靠 stream 和不可靠 datagram。
这正是 WebTransport 适合讨论的地方:它面向客户端与服务器通信,提供不同的传输原语,但不会自动替应用完成音频采集、编解码和播放。stream 提供可靠、有序的字节序列,消息边界仍由应用定义;datagram 保留每条数据报的边界,但不保证到达或顺序。通过 HTTP/3 的映射,它可以使用 QUIC 的能力。(Frindell et al., 2026; World Wide Web Consortium, 2026)
标准状态也要与机制分开看:截至本文写作日 2026 年 9 月 22 日,WebTransport API 已有 2026 年 7 月的 W3C Candidate Recommendation Snapshot,而 HTTP/3 映射仍是 IETF draft,并非已经发布的 RFC。理解其设计,不等于假设所有客户端、服务器和代理都支持相同部署组合。(Frindell et al., 2026; World Wide Web Consortium, 2026)
为什么有了可靠 stream,还需要 datagram
考虑白板每隔一小段时间发送一次完整光标位置。若第 41 次更新丢失,第 42 次已经到达,接收端通常希望立即使用第 42 次,而不是等第 41 次补齐。
可靠 stream 的字节交付语义并不允许随意跳过缺口;QUIC DATAGRAM 扩展则允许发送不进行传输层重传的数据。应用需要自行定义消息内容、序号和新鲜度规则。它仍受拥塞控制约束,也受数据报大小限制:不可靠的含义是“这条内容可以不补发”,不是“可以无限速、无限大地发送”。(Pauly et al., 2022)
对同一个协作会话,可以把持久编辑操作放到可靠 stream,把能被后续完整状态取代的光标位置放到 datagram。这样做并不是给后者更低的业务地位,而是承认两类数据的有效期不同。
与 WebSocket、SSE 怎样区分
| 接口 | 最自然的交互方式 | 选择时先问什么 |
|---|---|---|
| SSE / EventSource | 服务器持续向浏览器发送文本事件 | 是否只需要服务端推送? |
| WebSocket | 双向、有消息边界的连接 | 是否需要一个简单的双向可靠消息通道? |
| WebTransport | 客户端与服务器之间的多条 stream 与 datagram | 是否真的需要区分完整交付和过时可丢? |
| WebRTC | 实时媒体会话与 DataChannel | 是否需要媒体处理、候选路径发现或端点间连接? |
SSE 的自动重连和事件 ID 不会自动让服务器拥有永久事件历史。(WHATWG, n.d.) WebSocket 的经典映射建立在 TCP 上;后来也有 HTTP/2、HTTP/3 的承载方式,但单个 WebSocket 仍不因此变成多 stream 加不可靠 datagram 的接口。(Fette & Melnikov, 2011; Hamilton, 2022; McManus, 2018) 这些接口的选择应从交互模型出发,而不只是比较新旧。
MoQ 又在增加哪一层?
能发送媒体字节,还不等于适合把直播分发给大量订阅者。应用还需要表达“这是什么内容”“订阅哪一部分”“中继优先转发什么”。Media over QUIC(MoQ)探索的是这一层的发布订阅与媒体分发机制,可以利用 QUIC 或 WebTransport,而不是另造一个替代 UDP 的底层协议。
截至 2026 年 9 月 22 日,MoQ Transport 仍在 IETF draft 阶段。它适合用来理解设计趋势;讨论具体对象格式、优先级和恢复语义时,应固定草案版本,不能把变化中的细节写成既定标准。(Nandakumar et al., 2026)
gRPC:字节送到了,操作就完成了吗
白板保存服务已经有了一条可靠连接,为什么还需要 RPC?
因为字节本身没有告诉服务器:应该调用哪个方法、参数怎么解析、响应属于哪个请求。RPC 把这些约定组织成“调用远端方法”的接口。gRPC 通常用 Protocol Buffers 定义服务和消息,再生成客户端 stub 与服务端接口;RPC 是抽象,gRPC 是框架和协议机制,Protobuf 是默认采用的描述与编码工具,三者并不是同一个东西。(The gRPC Authors, n.d.)
例如,一次保存操作可以表达为 SaveBoard(board_id, revision, operations),而不必让每个调用者手工拼接和解析字节。gRPC 还支持单次请求响应、服务端流、客户端流和双向流。但接口像本地函数,不代表远端失败也能像本地函数一样判断。(The gRPC Authors, n.d.)
一次超时,至少有几种不同的过去
假设我们的协作产品还有一个简化的内部余额系统。客户端请求扣除
- 请求根本没到服务器,余额仍是
。 - 请求到了,但服务器在提交前失败,余额仍是
。 - 服务器已经提交扣款,余额变成
,但成功响应没有到达客户端。
客户端看见的都可能只是 timeout。可靠传输不会自动区分这三种业务历史;超时也不是“操作没有发生”的证明。(MIT 6.5840 Course Staff, 2026)
如果客户端直接重试“再减
先给业务意图命名,再让提交原子化
客户端可以给这次操作生成稳定的 operation_id,例如图中的 42,重试时继续使用它。服务端看到这个 ID 已经完成,就返回原来的结果,不再重复扣款。
但只在扣款后向内存集合里记一个 ID 还不够:如果恰好在扣款成功、记录 ID 之前崩溃,重启后仍然无法识别重试。于是,在这个所有账本状态都位于同一数据库的简化系统中,我们要把下面几件事放进同一事务:
- 通过唯一约束等机制占用操作 ID,并处理并发重复请求;
- 核对同一 ID 对应的请求参数,拒绝“同一 ID、不同金额”;
- 修改余额;
- 保存本次结果,以便重试时返回。
这样,提交成功意味着业务修改和去重结果一起存在;没有提交,则不应留下已经生效的半次修改。这里得到的是“重复请求不产生额外业务效果”,而不是让网络神奇地只传一次。(Featonby, n.d.)
这个设计还有明确的适用边界:去重记录需要保留策略;永久失联时不能保证最终完成;如果真实扣款发生在另一个外部支付服务,本地数据库事务就不能包住远端副作用,还需要远端支持相同的幂等键或额外协调。不要把一个本地事务例子直接升级成对所有分布式系统的 exactly-once 承诺。(Featonby, n.d.)
Deadline 和 retry 是预算,不是结果证明
调用还需要 deadline,防止客户端已经放弃后,下游仍无止境地消耗资源。(The gRPC Authors, n.d.b) 取消通常需要应用处理逻辑配合;它不等于回滚已经发生的修改,也不意味着客户端超时的瞬间服务器就必然停止。(The gRPC Authors, n.d.a)
重试同样必须有边界。gRPC 的透明重试针对框架能判断应用尚未处理等有限情形;一般重试策略需要配置可重试状态、次数和退避,不能把“框架支持 retry”理解成所有方法都可以安全重跑。一个非幂等业务方法,仍然需要前面那样的业务契约。(The gRPC Authors, n.d.d)
现在可以把几个“确认”分开:transport ACK 说明某些传输数据到达;RPC 响应可以报告方法的结果;业务是否持久生效,则取决于服务端怎样定义成功并提交状态。它们可能接近发生,但不是同一件事。
发布订阅:如果发送者不该认识每个接收者
RPC 很适合“请这个服务执行这个操作”。但另一类问题是:“这里发生了一次更新,谁关心谁来接收。”
例如,一个温度传感器的读数同时被仪表盘、空调和告警系统使用。如果传感器逐个调用它们,就需要维护每个消费者的地址和失败状态。发布订阅把接收者管理从生产者中分离:生产者发布数据,消费者声明兴趣,由中间层负责匹配和分发。
MQTT:消息的确认有明确范围
MQTT 使用 broker 和 topic 组织这种交互。传感器向 building/room-3/temperature 发布,消费者订阅相关主题;发布者不必逐个认识所有订阅者。它不是只能单向通信,每个客户端都可以承担发布与订阅角色。(Jain, 2021)
接下来仍然要问丢包和重复。MQTT 用不同 QoS 等级规定发送者与接收者之间的交付机制:
| QoS | 主要流程 | 协议交付语义 |
|---|---|---|
| 0 | PUBLISH | 至多一次,可能没有收到 |
| 1 | PUBLISH → PUBACK | 至少一次,可能重复 |
| 2 | PUBLISH → PUBREC → PUBREL → PUBCOMP | 在规定范围内恰好一次交付 |
这个范围是一个发送者到一个接收者。发布端到 broker、broker 到每位订阅者,是分别处理的交付过程,使用的 QoS 也可能不同。收到发布端的协议确认,不能推出所有订阅者已经各自完成了一次数据库更新。QoS 2 的去重状态同样不会自动与消费者的业务事务合并。(OASIS, 2019)
因此,不能用“这里用了 QoS 2”替代对端到端处理语义的设计。对于温度显示,重要的可能是当前值;对于收费事件,重要的则是持久记录和业务去重,即使二者都被装进 MQTT 消息。
DDS:可靠性之外,还要描述数据多久有用
机器人每
仅仅选择“可靠”还不能回答这个问题。我们还需要分别表达保留多少历史、样本何时过期,以及晚加入的接收者要不要得到此前的数据。
DDS 围绕带类型的数据和发布订阅关系组织通信,提供相应 QoS 策略;RTPS 则规定了 DDS 实现之间使用的一种互操作线协议。讨论 ROS 2 时也要分清:ROS 2 的中间件抽象并不等同于 DDS 标准本身。(Elwin, n.d.; Object Management Group, 2022)
以常见 QoS 名称为例,Reliability 关心尽力交付还是可靠交付,History 关心保留多少样本,Lifespan 关心样本有效期。Deadline 表达预期的更新间隔和违约检测,不是让网络自动保证所有数据准时到达。发送端提供的策略还需要满足接收端请求的兼容条件,并非两边所有配置必须字面相同。(ROS 2 Documentation Contributors, n.d.)
这里真正值得记住的不是策略名称,而是这些维度不能压缩成同一个“可靠性等级”。
CoAP:收到消息和完成请求可以显式分开
受限设备也可能需要“读取温度资源”“修改目标温度”这样的资源操作。CoAP 把这类请求响应语义带到受限网络环境,其基础 UDP 映射区分可确认与不可确认消息。
一个很能说明边界的设计是:服务端可以先发送空 ACK,表示消息收到,等操作完成后再单独发送响应。空 ACK 不是“温度已经设置成功”。(Shelby et al., 2014)
CoAP Observe 则允许客户端持续接收资源状态变化通知。它关心观察者跟上当前状态,不保证每个中间状态都成为不可遗漏的事件日志。因此,“观察当前温度”和“保存每笔交易”不能只因为都需要推送,就采用完全相同的交付假设。(Hartke, 2015)
回到白板:把协议组合起来
现在重新设计开头的在线白板,不需要先选一个包办全部通信的协议。可以按数据的含义拆开选择。
| 数据或操作 | 需要保持的性质 | 可以采用的组合与额外设计 |
|---|---|---|
| 页面、图片、文件 | 内容完整,允许一定等待 | HTTP/2 或 HTTP/3;恢复与缓存按应用需求设计 |
| 语音与视频 | 在播放时限内尽量连续 | WebRTC 媒体链路、拥塞适应、缓冲与丢包恢复 |
| 完整光标位置 | 优先新状态,旧状态可以失效 | DataChannel 或 WebTransport datagram,加序号与过期判断 |
| 持久白板操作 | 编辑不能静默丢失 | 可靠通道,加版本、确认、重连后的补同步 |
| 后端扣费或保存方法 | 可报告结果,重试不误执行 | RPC 加 deadline、操作 ID 和明确的事务边界 |
| 面向多个消费者的状态更新 | 发送者与消费者解耦 | MQTT 或 DDS,按状态有效期和业务需求选择 QoS |
这张表不是唯一架构。一个较简单的产品,完全可能用 HTTP 加 WebSocket 完成大部分功能;只有在确实需要媒体链路、独立 stream 或过时可丢的数据时,引入额外机制才有清晰收益。
选择之前,可以沿着同一条顺序追问:数据是完整文件、增量操作,还是可覆盖的状态?缺了前一条,后一条还能不能独立使用?晚到是否仍有价值?超时之后,远端可能已经做了什么?确认停在哪个边界?
QUIC 让独立的数据流不必因为别人的缺口一起等待;实时协议让数据的时间价值进入设计;RPC 和消息协议让通信拥有操作与分发语义。但应用最终仍要定义:什么必须被保留,什么可以过期,什么效果不能重复发生。
继续阅读
如果希望沿着本文的推导补齐基础,可以先读 Berkeley CS 168 的 TCP 设计。(Kao, n.d.b) 接着补齐拥塞控制,再进入下面几组材料。(Kao, n.d.a) 课程用于建立整体理解,具体线协议和安全性质则应与对应版本的标准一起阅读。
| 方向 | 课程或教材 | 适合带着什么问题读 |
|---|---|---|
| QUIC 与传输演进 | ETH Zürich Advanced Topics in Communication Networks,2025 年 transport 课件;Stanford CS 249I 的 Web Protocol Evolution | 为什么 HTTP 多路复用还需要传输层配合?握手与部署分别限制了什么? |
| 实时媒体 | UCSB CS 176C,Spring 2026 Lecture 12;WebRTC for the Curious | 如何分配播放预算?发现地址、验证路径、保护媒体为何是不同步骤? |
| RPC 与失败 | MIT 6.5840,2026 年 Threads and RPC;Cornell CS 4414 的 networking slides | 为什么一次超时不能证明远端没有执行?接口抽象隐藏了什么? |
| 物联网与机器人 | Washington University in St. Louis 的 MQTT 讲义;Northwestern ME495 的 DDS notes | 消费者是谁来管理?可靠性、历史与有效期怎样分别表达? |
ETH 的 2025 年课件适合连接传统 transport 与 QUIC。(ETH Networked Systems Group, 2025) Stanford 的材料适合看 Web 协议演进,不过其中 QUIC 握手部分包含早期设计,理解现代 IETF QUIC 时应以 RFC 9000/9001 为准。(Durumeric, 2025)
实时媒体可以先读 UCSB 的场景导向讲解。(Gupta, 2026) 再读 WebRTC for the Curious 的 connecting、securing、media communication 和 data communication 章节。(WebRTC for the Curious Contributors, n.d.) 涉及延迟计算时,始终先确认每个时间量的起止点。
MIT 的 RPC notes 值得完整阅读。(MIT 6.5840 Course Staff, 2026) Cornell 的 gRPC 部分更适合作为短简介,引用的是 2024 年秋季课程提供的 Spring 2023 课件。(Birman, 2023) MQTT 的课程材料采用 2021 年版本,具体 QoS 行为对照 MQTT 5.0。(Jain, 2021) Northwestern 的 DDS notes 则可以结合 ROS 2 的 QoS 配置,继续研究发布端和订阅端如何匹配。(Elwin, n.d.)