分块WebSocket传输

jAn*_*ndy 43 javascript specifications websocket node.js

因为我在更常规的基础上使用WebSocket连接,所以我对如何在幕后工作感兴趣.所以我在一段时间内深入研究了无穷无尽的规范文档,但到目前为止,我还无法找到任何有关分块传输流本身的信息.

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

阅读完之后,我立即设置了我的小Node WebSocket服务器.由于我有一个强大的Ajax历史(也在流媒体和Comet上),我的期望原来就像是," 在传输数据时必须有某种交互模式来读取数据 ".但那里我错了,不是吗?

我开始时很小,有4kb的数据.

服务器

testSocket.emit( 'data', new Array( 4096 ).join( 'X' ) );
Run Code Online (Sandbox Code Playgroud)

并且像预期的那样,它作为一个数据块到达客户端

客户

wsInstance.onmessage = function( data ) {
    console.log( data.length ); // 4095
};
Run Code Online (Sandbox Code Playgroud)

所以我增加了有效载荷,我实际上再次期待,在某些时候,客户端onmessage处理程序将重复发射,effectivley分组传输.但令我震惊的是,它从未发生过(节点服务器,在firefox,chromesafari客户端测试).我最大的有效载荷是80 MB

testSocket.emit( 'data', new Array( 1024*1024*80 ).join( 'X' ) );
Run Code Online (Sandbox Code Playgroud)

它仍然存在于客户端的一个大数据块中.当然,即使你有一个非常好的连接,这需要一段时间.这里的问题是

  • 是否有可能将这些流分块,类似于XHR readyState3模式
  • 单个ws数据框有任何大小限制吗?
  • 不应该传输如此大的有效载荷的websockets?(这会让我再次想知道为什么没有定义的最大尺寸)

我可能仍然从WebSockets的错误角度看,可能是发送大量数据的需要不存在,你应该在发送之前自己逻辑地分块/拆分任何数据?

obe*_*tet 80

首先,您需要在浏览器中区分WebSocket 协议和WebSocket API.

WebSocket协议的帧大小限制为2 ^ 63个八位字节,但WebSocket消息可以由无限数量的帧组成.

浏览器中的WebSocket API不公开基于帧的API或流API,而只公开基于消息的API.在将其提供给JavaScript之前,传入消息的有效负载始终完全缓冲(在浏览器的WebSocket实现中).

其他WebSocket实现的API可以提供对通过WebSocket协议传输的有效载荷的基于帧或流的访问.例如,AutobahnPython就是这样.您可以在这里的示例中阅读更多内容https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/streaming.

披露:我是Autobahn的原作者,也为Tavendo工作.

更多考虑因素

只要浏览器JS WebSocket API中没有帧/流API,您就只能接收/发送完整的WS消息.

单个(普通)WebSocket连接不能交错多个消息的有效负载.因此,即如果您使用大型邮件,那些邮件将按顺序发送,并且您仍然无法在大型邮件仍处于运行状态时在其间发送小邮件.

即将推出的WebSocket扩展(扩展是扩展协议的内置机制):WebSocket多路复用.这允许在单个底层TCP连接上具有多个(逻辑)WebSocket连接,这具有多个优点.

另请注意:您可以从一个单一的JS/HTML页面打开多个WS连接(在不同的底层技术合作计划),以一台目标服务器今天.

另请注意:您可以在应用程序层中自行"分块":将您的内容发送到较小的WS消息中,然后重新组装.

我同意,在一个理想的世界中,你在浏览器和WebSocket多路复用中都有消息/帧/流API.这将提供所有的力量和便利.

  • thx for赏金;)如果你从WHATWG得到一些有趣的反馈,我也会感兴趣(即在这里发表评论).. (3认同)
  • 因此,如果我的快速计算是正确的,那就是大约*83 TB*的帧大小,在这种情况下几乎等于无穷大.然而,没有(浏览器)API允许拦截传输流?听起来很奇怪. (2认同)
  • 是的.请记住,大多数没有流API的实现都有明确的(可配置的)帧(或消息)大小限制,或者因为内存不足而死亡.另外请记住,即使浏览器提供基于帧的API并且服务器会以小帧发送大型消息,中间人也可能会重新组合帧.因此,你真的想要JS中的消息,框架和基于流的API.它不在那里.您可以要求W3C指定一个;) (2认同)
  • @jAndy对WebSocket(RFC6455)的支持现在几乎是通用的,对WebSocket压缩的支持正在跟进(RFC现在已经完成).Rgd多路复用或具有帧/流支持的浏览器API:nope. (2认同)

Cod*_*ter 10

RFC 6455第1.1节:

这就是WebSocket协议提供的内容:[...] HTTP轮询的替代方法,用于从网页到远程服务器的双向通信.

如上所述,WebSockets用于网页和服务器之间的通信.请注意网页和网络浏览器之间的区别.正在使用的示例是浏览器游戏和聊天应用程序,他们挑选了许多小消息.

如果你想在一条消息中发送许多MB,我认为你没有按照预期的方式使用WebSockets.如果要传输文件,请使用Plain Old Http Request执行此操作,然后回答Content-Disposition以让浏览器下载文件.

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

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

RFC 6455第10.4节:

在从多个帧重新组装后,对于帧大小或总消息大小具有实现和/或平台特定限制的实现必须保护自己不超过这些限制.(例如,恶意端点可以尝试通过发送单个大帧(例如,大小为2**60)或通过发送长流小帧来耗尽其对等方的内存或发起拒绝服务攻击是一个碎片消息的一部分.)这样的实现应该对从多个帧重组后的帧大小和总消息大小施加限制.