让node.js及时发送数据报socket数据

Posted

技术标签:

【中文标题】让node.js及时发送数据报socket数据【英文标题】:Making node.js send datagram socket data in a timely manner 【发布时间】:2013-09-10 11:05:53 【问题描述】:

我有一个基于 Node-Webkit 的应用程序,它完成了一些繁重的 WebGL 工作(足以对高端 GPU 造成压力)、相当大量的 JS 处理,并且我在订单上发送OSC 数据约 4kb/s 的节点 dgram 模块到 scsynth 子进程来控制音频。

每 10 帧动画包含一个包中的 OSC 数据,并且音频可以很好地容忍它接收到的消息中的一些延迟或不规则性,但不是我所经历的规模。

不幸的是,我发现在调用socket.send(...) 和实际发送数据之间存在很大的延迟。似乎在某些情况下,调度程序对实际发送数据的优先级很低,以至于每个新数据包几乎无限期地停滞,然后突然以大型不受控制的突发释放,溢出scsynth 命令队列。

我无法将 udp 代码放入 WebWorker,因为 node.js 对象在该上下文中不起作用。我正在考虑尝试设置一个单独的窗口(因此,进程),仅负责将通过window.postMessage 接收到的数据转发到 UDP(反之亦然),但由于postMessage 本身也是异步的,并且另一个窗口如果它不可见,它本身可能具有低优先级,我想知道这是否可能提供很多好处。

我很确定主要问题在于 javascript 中的工作安排,而不是流程中的其他任何地方;我认为接收端没有特别的麻烦,尽管也许可以更仔细地评估。

这是一个简短的 sn-p 显示如何设置和使用套接字(包括收集一些关于发送回调的基本统计信息)。

udp = require('dgram').createSocket('udp4');
//...
var udpStats = lastSendDelay: 0, minSendDelay:Number.MAX_VALUE, maxSendDelay:-1, meanSendDelay:undefined;
var udpSend = function(buf) 
    var t = new Date();
    var wasSent = function(timeOfRequest) 
        return function(err) 
            if ((err)) sclog("UDP send Error: " + err);
            var t2 = new Date();
            var dt = t2 - timeOfRequest;
            udpStats.lastSendDelay = dt;
            udpStats.minSendDelay = Math.min(dt, udpStats.minSendDelay);
            udpStats.maxSendDelay = Math.max(dt, udpStats.maxSendDelay);
            udpStats.meanSendDelay = udpStats.meanSendDelay === undefined ? dt : (udpStats.meanSendDelay+dt)/2;
        ;
    ;

    udp.send(buf, 0, buf.length, UDP_PORT, 'localhost', wasSent(t));
;

【问题讨论】:

如果你能提供一个可运行的案例来重现这个,我会尝试修复它。 谢谢。实际的项目相当大而且多毛,但我也许可以强制以更简单的方式复制它,这也有助于消除我一直假设不相关的系统部分。不知道这需要多长时间,我现在只需要为实际项目使用静态音频文件,但仍然希望架构正常工作。我尝试过将 net 模块与 tcp 一起使用,但这不起作用;也许是另一个问题的主题。 我刚刚意识到我在和你说话,罗杰。再次感谢您提供帮助。我关于较低级别的 socket.send 的推理是否被认为是低优先级,因此未按计划执行? 我不认为它被安排在低优先级。顺便说一句,你的操作系统是什么? 我遇到的主要问题是 Windows 7 64bit。 【参考方案1】:

如果它对其他人有帮助,我们通过将对 requestAnimationFrame 的调用替换为超时为 1 毫秒的调用和 requestAnimationFrame 来解决此问题。这允许其他事情(例如 udp 发送/接收处理)在 1 毫秒处理时间内挤入线程,而不会显着影响整体 fps。

~~~

附言 使用 nw 8.5,我发现使用 setTimeout 延迟 requestAnimationFrame 效果最好,典型的 OSC 往返时间为 2 毫秒。使用 process.setTick 推迟 OSC 往返时间约为 20 毫秒。直接调用 requestAnimationFrame 导致 OSC 消息几乎完全阻塞。

使用 nw 12.1 没有问题,所有三个变体的行为几乎完全相同,典型的往返时间为 2 毫秒。

一段时间以来,我们一直被限制使用 nw 8.5,因为我们的 webGL 代码无法与 ANGLE 一起使用,并且更高版本的 nw 无法与 -use-gl=desktop 一起使用。使用最新版本的 nw,ANGLE 工作得更好,-use-gl=desktop 也能再次工作。

【讨论】:

process.nextTick() 呢? 正如 Peter T 所指出的,“其他事情”更具体地说是“在节点上下文中排队的事件”。谢谢你的建议,加布里埃尔。我将尝试 process.nextTick()。由于我们的应用程序是 GPU 绑定的,javascript cpu 成本相对较低,我怀疑它不会有太大的不同,但多学一点总是好的。 nextTick 现在甚至可以工作了。 *** 没有让我完成对最后一条评论的编辑... nextTick() 甚至可能不起作用。文档说:“这在开发 API 时很重要,您希望在构建对象之后,但在发生任何 I/O 之前,让用户有机会分配事件处理程序。” 原因setTimeout() 是为了确保 I/O 确实发生。

以上是关于让node.js及时发送数据报socket数据的主要内容,如果未能解决你的问题,请参考以下文章

在 node.js 中打开套接字时发送数据

如何将二进制数据从 Node.js socket.io 服务器发送到浏览器客户端?

使用 Socket.IO + Node.js + ZMQ 发送消息时发生内存泄漏

c# SocketIoClientDotNet, node js socket.IO

我可以通过socket.emit发送多少数据?

如何将数据从 Android 中的数据报套接字发送到 Node js 服务器?