WebSocket传输的分块处理

49

由于我更经常使用WebSocket连接,我对底层工作方式很感兴趣。所以我花了一段时间研究了无数的规范文件,但到目前为止,我仍然找不到有关将传输流本身分块的内容。

WebSocket协议称其为数据帧(描述了纯数据流,因此也称为非控制帧)。据我所理解的规范,没有定义最大长度和最大传输单元(MTU)值,这反过来意味着单个WebSocket数据帧可以包含无限数量的数据(如果我错了,请纠正我,我在这方面还是个学生)。

阅读完后,我立即设置了自己的小型Node WebSocket服务器。由于我有较强的Ajax历史记录(也包括流和Comet),我的期望原本是:“必须有某种交互模式来读取数据,同时进行传输”。但我错了,是吗?

我从小处开始,只有4kb的数据。

服务器

testSocket.emit( 'data', new Array( 4096 ).join( 'X' ) );

就像预期的那样,这个数据作为一个整体被发送到客户端。

客户端

wsInstance.onmessage = function( data ) {
    console.log( data.length ); // 4095
};

我增加了有效负载,并且实际上我预期,客户端的onmessage处理程序会重复触发,从而分块传输。但令我震惊的是,这从未发生过(在node-server上测试了firefoxchromesafari客户端)。最大有效负载为80 MB

testSocket.emit( 'data', new Array( 1024*1024*80 ).join( 'X' ) );

即使你的连接速度很好,它仍然会在客户端上以一个大数据块的形式到达。 当然,这需要一段时间。下面是问题:

  • 是否有可能像XHR readyState3模式那样对这些流进行分块
  • 单个ws数据帧是否有大小限制
  • WebSockets是否不应传输如此大的有效负载?(这让我再次想知道为什么没有定义最大大小)

我可能仍然从错误的角度看待WebSockets,可能发送大量数据的需求不存在,您应该在发送之前逻辑地自己拆分任何数据。

2个回答

89
首先,你需要区分WebSocket协议和浏览器中的WebSocket API。
WebSocket协议的帧大小限制为2^63个八位字节,但一个WebSocket消息可以由无限数量的帧组成。
浏览器中的WebSocket API没有暴露基于帧或流的API,只有基于消息的API。传入消息的有效负载总是在提供给JavaScript之前完全缓冲起来(在浏览器的WebSocket实现中)。
其他WebSocket实现的API可能会提供基于帧或流的访问方式,用于通过WebSocket协议传输的有效负载。例如,AutobahnPython就提供了这样的功能。你可以在这里的示例中了解更多信息:https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/streaming。
声明:我是Autobahn的原始作者,目前在Tavendo工作。
更多考虑事项:
只要浏览器的JavaScript WebSocket API中没有帧/流式API,你只能接收/发送完整的WebSocket消息。
一个(普通的)WebSocket连接不能交错多个消息的有效负载。例如,如果您使用大型消息,这些消息将按顺序传递,而在大型消息仍在传输过程中,您将无法发送小型消息。
还要注意:您可以从单个JS / HTML页面向单个目标服务器打开多个WS连接(通过不同的底层TCP)。
还要注意:您可以在应用层自己进行“分块”:将您的内容分成较小的WS消息并自行重新组装。
我同意,在理想的世界中,您将在浏览器中拥有消息/帧/流API以及WebSocket多路复用。这将提供所有的功能和便利性。

2
如果我的快速计算是正确的,那么这大约是 83 TB 的帧大小,在这种情况下几乎等于无限。然而,没有(浏览器)API允许拦截传输流?听起来很奇怪。 - jAndy
2
是的。请记住,大多数没有流API的实现都有显式(可配置的)帧(或消息)大小限制,否则将因缺乏内存而死机。还要记住,即使浏览器提供了基于帧的API并且服务器会在小帧中发送大消息,中间件也可能重新组装帧。因此,您确实需要JS中的消息、帧和流式API。它还没有出现。您可以向W3C提出要求指定一个;) - oberstet
3
感谢奖励;)如果你从WHATWG得到一些有趣的反馈,我也很感兴趣(即在这里留下评论)。 - oberstet
2
@jAndy 支持 WebSocket (RFC6455) 的现在几乎是普遍的,对于 WebSocket 压缩的支持也在跟进中(相关标准已经完成)。关于多路复用或者带有帧/流传输支持的浏览器 API:目前还没有。 - oberstet
1
似乎正在开发一个WebSocketStream API。 - Jeremy Field
显示剩余5条评论

10

RFC 6455 第1.1节:

WebSocket协议提供了以下内容:[...]从Web页面到远程服务器的双向通信的HTTP轮询替代方案。

正如所述,WebSocket用于Web页面和服务器之间的通信。请注意Web 页面和Web 浏览器之间的区别。例如,浏览器游戏和聊天应用程序,它们交换许多小消息。

如果您想在一条消息中发送许多MB,则认为您并没有使用WebSocket的预期方式。如果您想传输文件,请使用普通的Http请求进行传输,并使用Content-Disposition回复以让浏览器下载文件。

因此,如果您解释为什么要发送这么大量的数据,也许有人可以帮助想出比使用WebSocket更优雅的解决方案。

此外,客户端或服务器可能会拒绝过大的消息(尽管没有明确说明它将如何拒绝):

RFC 6455第10.4节:

实现具有实现和/或平台特定限制的大小限制,有关帧大小或多个帧重新组装后的总消息大小,必须保护自己免受超过这些限制的影响。(例如,恶意端点可以尝试通过发送单个大帧(例如,大小为2 ** 60)或发送许多小帧的长流以避免其同行的内存或发动拒绝服务攻击,这些小帧是分段消息的一部分。)此类实现应对帧大小和多个重新组合后的消息的总大小施加限制。


你的意思是,为了让一个用户向另一个用户发送文件,该用户应将文件发送到服务器以在某个地方暂时保存。然后通知接收者需要向HTTP端点发出请求以检索文件。一旦接收者检索到文件,就删除持久化的文件。而不是直接将文件发送给接收者,一旦接收者收到,立即发送,而不需要所有中间步骤? - Spankied
这就是Websockets的工作原理。如果您想要点对点通信,那么请使用支持穿越NAT等协议的协议进行打洞。例如,请参见https://dev59.com/pnNA5IYBdhLWcg3wQ7e4,WebRTC。 - CodeCaster
我正在开发一个国际象棋游戏,用户可以通过WS发送移动操作,以及其他游戏/房间状态。在游戏房间中,用户可以共享音乐文件。你的意思是我应该使用完全不同的协议来分享音乐,因为这不是WS的原本目的? - Spankied
@Spankied 如果你想创建一个点对点文件共享程序,请使用文件共享协议,而不是WebSockets。 - CodeCaster
最终将文件上传到S3并通知房间中的用户共享了歌曲。然后,用户可以发出HTTP请求来检索文件。在构建使用AWS的真正后端之前,我已经在WS上实现了所有这些功能,这限制了有效载荷大小。感谢您对我的信任。 - Spankied

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接