【求助】【再次描述问题】Nodejs WebSocket 消息有时延迟很久。
【求助】【再次描述问题】Nodejs WebSocket 消息有时延迟很久。
2014-8-19再次描述问题。
有时客户端在发送二进制数据时,服务端收到时少了一个字节,这时服务器会等待这个字节,到来之后才调用onMessage。 然而这个字节是在一段时间(20s)没有通信,服务器发送ping消息,客户端回应pong时。和pong一起收到的。 客户端发送的时候是没有问题的。 应该不是WebSocket包的原因。 有没有大神帮忙分析一下。。
###↓↓↓这是之前的描述 使用WebSocket 客户端发送message到服务器端,即使是在本地有时会有很长的延迟(10s左右)才收到
项目使用的websocket包是Worlize/WebSocket-Node 简单调试了一下源码,发现当需要接收的数据包多于一个的时候,第二个数据包会在发送后很久才到达。 (目前我还不清楚数据包是怎样分的,同样的请求数据有时一个包,有时两个包。)
源码里有关buffer的东西太多,有点弄不懂了。。
有没有遇到过同样问题的大神帮忙指点指点
今天把WebSocket包换成了einaros/ws,问题反而更严重了。
在ws/lib/Receiver.js中有一个expectData方法,其功能就是按照长度length保存WebSocket包的data
Receiver.prototype.expectData = function(length, handler) {
if (length == 0) {
handler(null);
return;
}
this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation);
this.expectHandler = handler;
var toRead = length;//需要读取的长度
while (toRead > 0 && this.overflow.length > 0) {
var fromOverflow = this.overflow.pop();//第一个包(一般只有一个,而且fromOverflow.length=toRead,
//但是问题出现的时候fromOverflow.length=toRead-1,也就是少一个字节)
//正常情况下会再循环一次读取下一个字节,可问题是下一个包还没收到 this.overflow.length = 0,所以并没有循环读取,而是直接返回了
if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
var read = Math.min(fromOverflow.length, toRead);
fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
};
为什么第二个包接收到的这么慢呢? 再深层的代码就是nodejs的net.js 和 _stream_readable.js了。
再次求助啊。。谢谢大家了
根据您的描述,这个问题主要涉及到WebSocket消息传输过程中可能出现的数据包丢失或延迟。这种情况可能是由于网络问题、缓冲区管理不当或WebSocket库本身的bug导致的。
分析与建议
1. 网络问题
首先,检查网络连接是否稳定。如果网络不稳定,可能会导致数据包丢失或延迟。可以尝试在不同的网络环境下测试,以确定是否是网络问题。
2. 缓冲区管理
从您的描述来看,问题可能出在缓冲区管理上。WebSocket协议在传输数据时,会将数据分割成多个数据包进行传输。接收端需要正确处理这些数据包,否则可能导致数据丢失或延迟。
3. WebSocket库
您提到使用的是einaros/ws
库,并且在更换库后问题变得更严重。这可能是因为不同库对WebSocket协议的实现方式不同,导致某些情况下处理不当。
示例代码及解释
以下是一个简单的Node.js WebSocket服务器和客户端示例,使用ws
库。您可以尝试使用这个库并查看是否仍然存在延迟问题。
服务器端代码
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', ws => {
console.log('Client connected');
ws.on('message', message => {
console.log(`Received message: ${message}`);
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
console.log('WebSocket server is running on ws://localhost:8080');
客户端代码
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8080');
ws.on('open', () => {
console.log('Connected to server');
setInterval(() => {
ws.send(new Uint8Array([1, 2, 3, 4]));
console.log('Sent message');
}, 1000);
});
ws.on('message', message => {
console.log(`Received message: ${message}`);
});
ws.on('close', () => {
console.log('Disconnected from server');
});
调试与优化
- 日志记录:在关键位置添加日志记录,以便更好地理解数据包的传输过程。
- 超时设置:增加超时时间,确保在网络不稳定的情况下不会导致数据包丢失。
- 错误处理:增强错误处理逻辑,确保在网络异常时能够及时恢复。
希望这些信息能帮助您解决问题。如果问题仍然存在,请提供更多详细信息以便进一步分析。
好像用socket.io
会简单些.
如果只要纯websoscket
的话, 这个库会简单些ws
我之前有弄个,怎么感觉没什么延时 用的是 ws
再次求助啊。。谢谢大家了
这库的代码没法看,说说思路看看你能不能找到问题吧 1.抓包检查少的字节是否确实已经从客户端发送出去了,如果未发送,检查客户端的发送buffer的实现 2.在服务端抓包看是否收到了这个字节,如果收到了,检查服务端接收buffer的实现,因为消息可能是达到一个阈值才会触发onMessage事件的 3.在库的Receiver和buffer实现里多打一点log看
最近才用websocket-node写了个服务器 https://github.com/Hi-Rube/RChat-Server android客户端2g网络发消息秒速到达,没觉得有延迟啊~
请问楼主找到解决方案来吗?求教!
是不是采用了默认的Nagle算法,该算法在网络中存在极少数有效数据的时候不会立即发送,只有等到下一批数据到来时才会发,可以通过socket.setNoDelay(true)解除该算法
expectData需要传入 length 参数,会不会是这个参数有问题,少了1?
从你的描述来看,问题可能出在WebSocket库对数据包的处理方式上。在einaros/ws
库中,Receiver.prototype.expectData
方法用于处理接收到的数据片段,并根据预期的数据长度来处理这些片段。如果某些片段没有及时到达,可能会导致延迟。
为了简化问题,你可以尝试以下几点:
-
使用其他WebSocket库:有时候更换不同的WebSocket库可以解决一些特定的问题。例如,可以尝试使用
ws
库的最新版本,或者尝试其他如socket.io
这样的库。 -
增加心跳检测:在长时间没有消息传递时,增加心跳检测可以帮助保持连接活跃。可以在客户端和服务端都实现一个心跳机制,通过定期发送和响应心跳消息来确保连接稳定。
-
优化缓冲区管理:在接收数据时,可以检查是否有足够的缓冲空间来处理所有数据。如果有需要,可以调整缓冲区大小或优化缓冲区的分配策略。
-
调试和日志记录:增加详细的日志记录可以帮助你追踪数据包的接收情况,找到延迟的具体原因。
以下是使用ws
库的一个简单示例,演示如何设置一个基本的心跳机制:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', ws => {
let timeout;
const heartbeat = () => {
clearTimeout(timeout);
timeout = setTimeout(() => ws.terminate(), 30000); // 如果30秒没有收到消息,则断开连接
};
ws.on('pong', heartbeat);
ws.on('message', message => {
console.log('received: %s', message);
heartbeat(); // 接收到消息后重置心跳计时器
});
heartbeat();
});
这个示例展示了如何在ws
库中设置心跳机制。希望这些建议对你有所帮助!