问题 通过websocket接收图像


我在用 websockify 将图像从python服务器显示到HTML5画布。

我认为我已经设法成功从我的python服务器发送图像,但我无法将图像显示到我的画布。

我认为问题与我试图在画布上显示的字节数有关,我相信我不会等到收到整个图像然后将图像显示到画布上。

到现在为止我有:

关于消息功能。当我发送图像时,我得到12 MESSAGERECEIVED 在控制台

  ws.on('message', function () {
    //console.log("MESSAGERECEIVED!")
            msg(ws.rQshiftStr());
  });

我收到字符串的msg函数,我试图在画布上显示它。我为每张图片调用了12次方法。刺痛的形式是 'xÙõKþ°pãüCY :

function msg(str) {
        //console.log(str);
        console.log("RELOAD");

        var ctx = cv.getContext('2d');
        var img = new Image();
        //console.log(str);
        img.src = "data:image/png;base64," + str;
        img.onload = function () {
            ctx.drawImage(img,0,0);
        }
    }

对于如何解决这个问题,有任何的建议吗?


5521
2018-02-15 11:03


起源

除非客户端不支持hybi草稿,否则不应将图像作为base64传输到websockets上。 Base64将产生33%的开销。因此优选发送二进制。 - einaros
我认为 websockify 将通过它传输的所有内容转换为base64。 - glarkou
如果是这样的话,那太可怕了。寻找另一个websocket实现。 - einaros
@einaros,我在下面的答案中更全面地解决了base64编码/解码问题,但总的来说,websockify的重点是透明地在大多数浏览器中启用二进制数据(不仅仅是现代浏览器)。顺便说一句,好的工作 github.com/einaros/ws。我计划在不久的将来更新我的webockify节点实现(主要是python,但我还有其他几个示例实现)来使用你的lib。 - kanaka


答案:


websockify + websock.js的重点是透明地支持流式二进制数据(更多内容见下文)。从接收队列中获取的数据已经被base64解码。但是,数据URI方案需要base64编码的字符串,因此您需要将图像数据编码为base64。您可以使用内置的window.btoa()对base64编码二进制编码的字符串:

img.src = "data:image/png;base64," + window.btoa(str);

或者,为了提高效率,您可以使用include / base64.js中的Base64模块,但是您需要传递一个像rQshiftBytes那样的字节数组:

msg(ws.rQshiftBytes());

img.src = "data:image/png;base64," + Base64.encode(data);  // str -> data

关于在websockify中使用base64:

Websockify使用base64对数据进行编码,以支持不直接支持二进制数据的浏览器。除了像iOS Safari和桌面Safari这样流行的Hixie浏览器之外,一些浏览器版本在野外使用HyBi但缺少二进制支持。不幸的是,就Chrome而言,他们同时也有一个WebIDL错误,这会阻止在建立连接之前检测二进制支持。

另外,在Opera,firefox 3.6-5,IE 8和9上使用WebSockets的主要选项是 网络插座JS。 web-socket-js支持HyBi,但没有二进制支持,可能不会,因为它所针对的大多数旧版浏览器不支持本机二进制类型(Blob和Typed Arrays)。

支持HyBi和二进制数据的浏览器的市场份额目前相当低。但是,将来,Websockify将检测(通过对象检测或浏览器版本检测)浏览器是否支持二进制数据并直接使用该支持而无需base64编码/解码。

base64编码/解码的延迟(和CPU)开销非常低,并且无论如何通常会被网络延迟淘汰。 base64编码数据的带宽开销约为25%(即原始数据变大33%),但它确实在基本上所有浏览器上通过WebSockets提供二进制数据(使用web-socket-js polyfill / shim,它是如果需要,可以通过websockify透明地使用)。


11
2018-02-15 14:43



再次感谢您的详细结果。能否请您举例说明如何确定图像是否完全传输?更具体地说,在python中,我使用缓冲区大小为1024的常规套接字发送数据。如果我有一个大于缓冲区的图像,那么它将以1024的部分发送。在浏览器中,这将触发12次(如果我有12kb)websocket消息事件的图像。如何组合所有这些消息来重建图像?再次感谢。 - glarkou
WebSocketServer.send_frames(bufs)获取“缓冲区”列表。每个缓冲区将作为单个消息(onmessage的一个消息)传递给客户端(浏览器)。如果您从onmessage中的接收队列中移除所有内容,那么您将获得完整的消息。如果您使用较小的块调用send_frames(或完全绕过send_frames),则需要在协议中添加一些重构消息的机制(即在每个消息/图像之前添加长度前缀)。但这是复制WebSocket功能,因此我建议将send_frames与整个消息/图像一起使用。 - kanaka
不幸的是,我无法理解如何做到这一点。目前我有一个发送图像的python服务器 Server 1 我有 websockify 继续前进 Server 2。当我收到我试图做的消息时 var arr = ws.rQshiftBytes(ws.rQlen()), str="", chr; while (arr.length > 0) { chr = arr.shift(); str += String.fromCharCode(chr); } //console.log(str); msg(str); 不幸。 ws.on('message', function () 从服务器发送的每个缓冲区调用,因此我无法确定图片何时准备好以便在画布上绘制它。 - glarkou
啊,我以为你是将websockify中的websocket.py作为python服务器的一部分。当您运行websockify作为WebSocket到TCP桥接时,您会遇到流/碎片(只是TCP的性质),因此您必须在两个端点之间自己进行帧/消息处理。您可以将websocket.py组合到python服务器中并直接使用send_frames,或者将框架添加到“协议”中。如果您在PNG图像中发送的唯一内容,则可以解析长度。否则,你将不得不从python服务器添加自己的框架。 - kanaka
既然这正在成为一个对话,也许你会考虑用websockify提交一个问题来继续这里? - kanaka