WebTransport 怎样选择双向流和 Datagram
我第一次把实时面板从单一 WebSocket 连接拆成 WebTransport 时,最容易混淆的不是连接 API,而是“所有消息都走同一条通道”。告警确认、配置变更和实时坐标对可靠性的要求并不一样。
判断方法可以先记成一句话:必须到达、必须保持顺序的数据,用双向流;允许丢失、下一次状态可以覆盖上一次的数据,用 Datagram。双向流也不是现成的消息队列,它只负责可靠的有序字节,应用层仍要处理消息边界。
官方文档:https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
选择 WebTransport 通道时,先看业务数据能不能丢,再看它是否需要顺序;不要先被“低延迟”三个字带着走。
我先按数据性质拆开这两个通道
同一个 WebTransport 会话可以创建可靠的双向流,也可以通过 transport.datagrams 发送不可靠数据。双向流提供可读和可写两端,适合聊天文本、订单状态、配置变更这类必须完整接收的消息;Datagram 更适合“只要最新值”的数据,例如游戏位置、鼠标轨迹或实时仪表盘采样。
| 判断维度 | 双向流 | Datagram |
|---|---|---|
| 到达保证 | 可靠传输 | 允许丢失 |
| 顺序保证 | 按字节顺序交付 | 不保证顺序 |
| 适合数据 | 命令、文本、结果、配置 | 瞬时状态、采样、可覆盖更新 |
| 应用层工作 | 需要 framing 区分消息 | 需要接受丢包和乱序 |

我在项目里会先给消息写一个“能不能重放”的判断:用户点击保存后返回的结果不能丢,所以走双向流;播放器已经产生了新位置,旧位置再送达也没有意义,所以可以走 Datagram。这个判断比单纯比较 API 名称更稳定。
我在可靠消息里使用双向流时,额外补一层消息边界
流的可靠性只说明字节不会随意丢失或乱序,不代表发送端的一次 write() 就会对应接收端的一次 read()。多个写入可能合并,一次写入也可能被拆开,所以聊天消息、JSON 命令这类内容要在字节流之上定义 framing。
function encodeMessage(text) {
// 统一使用 UTF-8,并把消息长度放在前四个字节
const body = new TextEncoder().encode(text)
const frame = new Uint8Array(4 + body.length)
new DataView(frame.buffer).setUint32(0, body.length)
frame.set(body, 4)
return frame
}
async function sendMessage(transport, text) {
// 双向流保证字节可靠有序,但不替应用层定义消息边界
const stream = await transport.createBidirectionalStream()
const writer = stream.writable.getWriter()
try {
await writer.write(encodeMessage(text))
} finally {
// 释放 writer,避免长时间占用这个流的写入端
writer.releaseLock()
}
}
示例只展示发送侧的协议形状:长度前缀是四字节无符号整数,接收侧需要持续把 ReadableStream 的字节放入缓冲区,至少积累到完整长度后再交给 JSON 解析器或业务处理器。不要把一次 read() 当作一条完整消息。

Datagram 适合会被新状态覆盖的实时数据
Datagram 的优势是不用为过期状态排长队,但代价是应用必须接受数据可能没有到达,也可能顺序变化。我通常只把它用于“晚到就没有价值”的数据:例如每 50 毫秒发送一次光标位置,客户端收到新位置后就可以覆盖旧位置。
async function sendCursor(transport, x, y) {
// 坐标是瞬时状态,旧值晚到时不值得阻塞新值
const payload = JSON.stringify({ x, y, sentAt: Date.now() })
const writer = transport.datagrams.writable.getWriter()
try {
await writer.write(new TextEncoder().encode(payload))
} finally {
// 每次发送完成后释放 writer,避免把无关状态长期锁住
writer.releaseLock()
}
}
如果消息代表扣款、权限变更、聊天正文或服务端确认,就不要因为它“看起来很小”而使用 Datagram。丢一条坐标通常只会让画面短暂不精确,丢一条确认结果却可能让两端状态永久分叉。
上手前我会检查这三个条件
- 安全上下文:浏览器接口要求 HTTPS 等安全上下文,开发环境要准备可信的本地 HTTPS 方案。
- 服务端能力:WebTransport 依赖 HTTP/3 服务端和对应的会话处理,不能只把 WebSocket URL 原样替换成新构造器。
- 浏览器支持:当前 MDN 将 WebTransport 标为 Baseline 2026,但旧设备和部分浏览器仍可能不完整支持,生产页面应保留 WebSocket 或 HTTP 长轮询的降级路径。
async function chooseChannel(url, message) {
// 连接建立后再读取 reliability,避免把 pending 当成最终能力
const transport = new WebTransport(url)
await transport.ready
if (transport.reliability === 'reliable-only') {
// 只能使用可靠传输时,状态更新也要走流或退回旧方案
return { transport, channel: 'stream' }
}
// 只有允许不可靠传输时,才把可覆盖状态交给 Datagram
return { transport, channel: message.replaceable ? 'datagram' : 'stream' }
}
我的落地清单很短:先把消息分成“不可丢”和“可覆盖”两类;不可丢的数据定义 framing 并处理断线重建;可覆盖的数据记录序号或时间戳,收到旧包时直接丢弃;最后为不支持 WebTransport 的环境保留备用传输。
常见问题
双向流比 Datagram 更快吗?
不能只用“快慢”概括。Datagram 不为可靠性和顺序等待,适合低延迟状态;双向流提供可靠有序字节,适合不能丢的业务消息。选择应由数据价值决定。
双向流为什么还需要长度前缀?
因为流提供的是连续字节序列,不保留应用层消息边界。长度前缀、分隔符或固定长度协议都可以完成 framing,关键是发送和接收两端使用同一套规则。
Datagram 丢包后要不要自动重发?
如果每个数据包都是可替换状态,通常直接等待下一次采样更合理;如果必须重发才能保证业务完成,这条消息本质上已经需要可靠语义,应改用双向流或在应用层明确设计确认与重试。
总结一下:双向流负责可靠、有序的字节,Datagram 负责允许丢失的低延迟状态;双向流上面还要自己定义消息边界。把“能不能丢”和“是否需要顺序”写进协议设计,WebTransport 的两种通道就不会因为名称相近而被混用。
Web Streams 怎样处理背压和取消
- 上一篇
- Web Streams 怎样处理背压和取消
- 下一篇
- WebSocket 心跳怎样避免页面休眠后误判断线
-
- 文章 · 前端 | 1小时前 |
- 前端 source map 发布怎样保护源码边界
- 194浏览 收藏
-
- 文章 · 前端 | 1小时前 | websocket · javascript · 前端工程 · websocket 自动重连 心跳 visibilitychange 后台标签页
- WebSocket 心跳怎样避免页面休眠后误判断线
- 483浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- Web Streams 怎样处理背压和取消
- 175浏览 收藏
-
- 文章 · 前端 | 1小时前 | VUE watch 响应式 Composition API reactive
- Vue watch 怎样只监听对象的一个字段
- 297浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- React Suspense 数据加载怎样避免瀑布请求
- 368浏览 收藏
-
- 文章 · 前端 | 1小时前 | typescript · javascript · 前端开发 · 类型系统 · TypeScript any 类型收窄 UNKNOWN 类型守卫
- TypeScript unknown 和 any 怎样选择
- 230浏览 收藏
-
- 文章 · 前端 | 1小时前 | 数组 · typescript · 前端开发 · 类型系统 · TypeScript 数组索引 类型收窄 noUncheckedIndexedAccess
- TypeScript noUncheckedIndexedAccess 开启后的数组读取处理方案
- 435浏览 收藏
-
- 文章 · 前端 | 1小时前 | 环境变量 · javascript · 前端工程 · 环境变量 vite 运行时配置 前端构建 import.meta.env
- Vite 环境变量为什么不能在运行时直接修改
- 413浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- Vite 依赖预构建缓存失效怎样定位
- 236浏览 收藏
-
- 文章 · 前端 | 16小时前 | 前端 · Web Components LiT 响应式更新 前端组件 updateComplete
- Lit updateComplete怎样等待组件更新结束
- 425浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 471次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 530次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 550次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 501次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 318次使用
-
- Go语言对前端领域的入侵WebAssembly运行原理
- 2022-12-31 130浏览
-
- Go Server-Sent Events 怎么正确刷新事件并关闭连接
- 2026-09-07 491浏览
-
- go+gin 静态资源路由与后端api路由冲突如何解决?
- 2023-02-24 414浏览
-
- 分片上传文件,后端接收怎么生成了一个文件名为blob的文件?
- 2023-01-09 163浏览
-
- 除了cookie之外,还能用什么方法做验证码功能?
- 2023-01-22 137浏览
