当服务器发送WebSocket消息并且数据包丢失时,客户端将不会看到任何消息,直到服务器意识到数据包已丢失、重新传输等并且它实际到达客户端...正如你所想象的那样,这可能会导致实时应用程序中不必要的大延迟。
我知道这是设计上的问题,因为TCP确保数据包按正确顺序传递。但我想知道是否有类似于Socket.IO的库可以绕过这种机制。从头开始编写这样的东西似乎很费力。
通过绕过,我的意思是例如使用UDP而不是TCP,使用新的WebRTC功能或者更简单地,只需创建多个WebSocket连接,并确保连续的消息通过不同的连接发送。我知道这样客户端可能会收到过时的信息,但它可以轻松地通过忽略它们来进行补偿。你只需要给每个消息一个递增的ID。
例如,一个Socket.IO的包装器就很好了。它具有相同的接口,但在内部创建多个连接。我尝试着为此编写一个包装器类,但我真的不确定如何正确地在包装器和Socket.IO实例之间传递事件。
我的意思是,如果我简单地将所有在套接字上触发的事件传递到包装器类,并将所有在包装器类上触发的事件传递给其中一个Socket.IO实例,那么每个事件都会永远循环。
const EventEmitter = require('events');
const Server = require('socket.io');
class ServerWrapper extends EventEmitter {
constructor() {
/* instanciation manual:
new ServerWrapper(httpServer[, options][, connectionCount])
new ServerWrapper(port[, options][, connectionCount])
new ServerWrapper(options[, connectionCount])
(connectionCount is the number of socket.io instances that will be used)
*/
let port, srv, opts; // not really necessary
let connCount = 5; //default
let args = arguments;
// The following if statements are used to maintain full compatibility with the original socket.io constructor (https://socket.io/docs/server-api/)
if (arguments.length === 0)
return;
else if (arguments.length === 1)
opts = arguments[0];
else if (arguments.length === 2) {
if (typeof arguments[0] === 'object' && arguments[1] === 'object') {
srv = arguments[0];
opts = arguments[1];
} else if (typeof arguments[0] === 'number' && arguments[1] === 'object') {
port = arguments[0];
opts = arguments[1];
} else if (typeof arguments[0] === 'object' && arguments[1] === 'number') {
opts = arguments[0];
connCount = arguments[1];
args = arguments.pop();
}
} else if (arguments.length === 3) {
opts = arguments[1];
connCount = arguments[2];
if (typeof arguments[0] === 'number')
port = arguments[0];
else
srv = arguments[0];
args = arguments.pop();
}
// Create X socket.io instances and store them in this array
this._io = [];
for (let i=0; i<connCount; i++)
this._io.push(new Server(args));
// Pass all socket.io events to this wrapper class
this._io.forEach(io=>{
io.on("*",this.emit);
});
// Pass all events fired on this wrapper class to one of the socket.io instances:
this.nextConn = 0;
this.on("*", (event,data) => {
this._io[this.nextConn].emit(...arguments);
this.nextConn++;
if (this.nextConn >= this.connCount)
this.nextConn = 0;
});
let sioProps = ['sockets'];
sioProps.forEach(prop=>{ // map all socket.io properties from the first instance to 'this[prop]'
this[prop] = this._io[0][prop];
});
let sioMethods = ['seveClient','path','adapter','origins','attach','listen','bind','onconnection','of','close'];
sioMethods.forEach(fName=>{ // redirect all socket.io function calls to all the socket.io instances
this[fName] = () => {
this._io.forEach(io=>{
this[fName] = io[fName](...arguments);
});
};
});
}
}
module.exports = ServerWrapper;