Socket.io 1.x:仅使用WebSockets?

时间:2015-01-30 15:01:15

标签: javascript websocket socket.io

我们正在开发一个仅在现代浏览器(IE10 +)上运行的Web应用程序,原因各不相同。

我们实现的功能之一是Socket.io 1.x.但是,默认情况下,Socket.io客户端会尝试支持较旧的浏览器,因此它会启动与长轮询的连接,然后将其更新到WebSockets。这是浪费时间和资源,因为我们知道浏览器支持WS。

我已经四处寻找了,我只能找到this wiki page,但是,这是关于Socket.io 0.9。

最终,我找到the documentation for engine.io-client(Socket.io-client基于1.x分支)。这是我写的代码,似乎正在工作。但是,我想知道它是否正确或者我是否做错了什么:

io.connect('https://...', {
    upgrade: false,
    transports: ['websocket']
})

奇怪的是,仅将transports属性设置为websockets的数组只是不够;我还必须禁用upgrade。这是对的吗?

更新

我做了一些新的发现。

transports设置为['websocket'],启用upgrade时不会产生任何差异。这是正常的吗?

4 个答案:

答案 0 :(得分:53)

有两种类型的"升级"发生在socket.io上。首先(在socket.io 1.0+中),socket.io启动所有与http轮询请求的连接,它实际上可能只用一个http请求交换一些初始数据。然后,在此之后的某个时刻,它将尝试实际启动webSocket连接。 webSocket连接是通过发送指定upgrade: websocket标头的特定类型的http请求来完成的,然后服务器可以适当地响应它是否支持websocket。如果服务器同意升级,那么该特定的http连接将被升级"到webSocket协议。此时,客户端知道webSocket受支持并且它停止使用轮询http请求,从而完成其upgrade到webSocket。

您可以通过在客户端上执行此操作来完全阻止初始http轮询:

var socket = io({transports: ['websocket'], upgrade: false});

这将阻止来自您自己的合作客户端的轮询连接。如果要阻止任何客户端使用轮询,则可以将其添加到服务器:

io.set('transports', ['websocket']);

但是,如果您在服务器上设置此项,则最初使用http轮询连接的socket.io客户端根本不起作用。因此,这应该只与客户端中的正确设置匹配,以便客户端永远不会以轮询开始。

这将告诉两端你只想使用webSockets而socket.io将在开头跳过额外的http轮询。公平警告,这样做需要webSocket支持,所以这排除了与尚未支持webSocket的旧版IE兼容。如果你想保持兼容性,那么就让socket.io最初用几个http请求来做这件事。


以下是有关协议从http升级到webSocket的更多信息。

webSockets协议使用HTTP连接启动每个webSocket。这就是所有webSockets的工作方式。该HTTP连接包含一些标题,表明浏览器会像#34;升级到webSockets协议。如果服务器支持该协议,则它会响应告知客户端它将升级到webSocket协议,然后该套接字将从HTTP协议切换到webSocket协议。这就是webSocket连接的设计方式。因此,您看到以HTTP连接开头的webSocket连接的事实是100%正常。

您可以将socket.io配置为永远不会使用长轮询,如果这让您感觉更好,但这不会改变webSocket连接仍然以HTTP连接开始然后升级到webSocket协议的事实,它将会不提高支持webSockets的现代浏览器的操作效率。但是,它会使您的连接无法在旧版浏览器中使用。

答案 1 :(得分:29)

要告诉Socket.IO首先只使用WebSocket而不是几个XHR请求,只需将其添加到节点服务器:

io.set('transports', ['websocket']);

在客户端上添加:

var socket = io({transports: ['websocket']});

这告诉Socket.IO只使用WebSocket协议而不是别的;它更干净,更快,并且在客户端和服务器端使用更少的资源。

现在,您只会在网络请求列表中看到一个WebSocket连接,请记住IE9和早期版本不能使用WebSocket。

答案 2 :(得分:19)

我发布了答案,因为接受的答案不正确 - 它将Socket.IO从长轮询AJAX升级到WebSocket与WSS协议“连接:升级”请求混淆。问题不在于WebSocket连接是以HTTP身份启动并升级到WebSocket - 它怎么可能不是? - 但是,即使在支持WebSocket的浏览器上,Socket.IO也会以长轮询AJAX连接开始,并且只在交换一些流量后才进行升级。在Firefox或Chrome的开发者工具中很容易看到。

该问题的作者在他的观察中是正确的。 Socket.IO中的“升级”并不是指HTTP到WSS协议升级,因为它经常被误解,而是指从长轮询AJAX连接升级到WebSocket的Socket.IO连接。如果您已经开始使用WebSocket(这不是默认设置),那么升级false无效,因为您不需要升级。如果您从轮询开始并禁用升级,那么它将保持这种状态,并且不会升级到WebSocket。

如果您想避免以长轮询开始,请参阅 arnold Nick Steele 的答案。我将更详细地解释发生了什么。

这是我在my experiments中使用简单的WebSocket和Socket.IO应用程序观察到的:

的WebSocket

2个请求,1.50 KB,0.05 s

从这2个请求中:

  1. HTML页面本身
  2. 连接升级到WebSocket
  3. (连接升级请求在具有101切换协议响应的开发人员工具上可见。)

    Socket.IO

    6个请求,181.56 KB,0.25 s

    根据这6项要求:

    1. HTML页面本身
    2. Socket.IO的JavaScript(180千字节)
    3. 首次长轮询AJAX请求
    4. 第二次长轮询AJAX请求
    5. 第三次长轮询AJAX请求
    6. 连接升级到WebSocket
    7. 详细

      我在localhost上获得的WebSocket结果:

      WebSocket results - websocket-vs-socket.io module

      我在localhost上获得的Socket.IO结果:

      Socket.IO results - websocket-vs-socket.io module

      自己测试

      我发布了代码on npmon GitHub,您可以自行运行:

      # Install:
      npm i -g websocket-vs-socket.io
      # Run the server:
      websocket-vs-socket.io
      

      并遵循限制。要卸载:

      # Uninstall:
      npm rm -g websocket-vs-socket.io
      

      有关详细信息,请参阅this answer

答案 3 :(得分:8)

我认为我应该添加上面接受的答案,好像有人想要消除XHR轮询传输并立即启动websockets。下面的代码只是为了说明实现:

var url = serverUrl + "/ssClients"  //ssClients is the socket.io namespace

var connectionOptions =  {
    "force new connection" : true,
    "reconnection": true,
    "reconnectionDelay": 2000,                  //starts with 2 secs delay, then 4, 6, 8, until 60 where it stays forever until it reconnects
    "reconnectionDelayMax" : 60000,             //1 minute maximum delay between connections
    "reconnectionAttempts": "Infinity",         //to prevent dead clients, having the user to having to manually reconnect after a server restart.
    "timeout" : 10000,                           //before connect_error and connect_timeout are emitted.
    "transports" : ["websocket"]                //forces the transport to be only websocket. Server needs to be setup as well/
}
var socket = require("socket.io-client")(url, connectionOptions); 

socket.on("connect", function (_socket) {
    logger.info("Client connected to server: " + clientName);
    logger.info("Transport being used: " + socket.io.engine.transport.name);

    socket.emit("join", clientName, function(_socketId) {  //tell the server the client name
        logger.info("Client received acknowledgement from server: " + _socketId);
        logger.info("Transport being used after acknowledgement: " + socket.io.engine.transport.name);

    });
});

设置服务器后,您将看到:

2015-10-23T19:04:30.076Z - info:    Client connected to server: someClientId 
2015-10-23T19:04:30.077Z - info:    Transport being used: websocket 
2015-10-23T19:04:30.081Z - info:    Client received acknowledgement from server: aMH0SmW8CbiL8w5RAAAA
2015-10-23T19:04:30.081Z - info:    Transport being used after acknowledgement: websocket

如果你不强迫运输,你会看到“轮询”而不是websocket。但是,这不会仅在客户端发生,服务器也必须设置:

var io = require("socket.io")(server, { adapter: adapter, log: false }); //attach io to existing Express (http) server
..
io.set('transports', ['websocket']); //forces client to connect as websockets. If client tries xhr polling, it won't connect.

<强>危险

如果客户端确实支持websocket协议,则不会发生连接,客户端将报告xhr poll error

这对我来说非常合适,因为我可以控制我拥有的客户,因此我可以立即强制使用websockets,我相信这是原始问题所要求的。我希望这可以帮助那些人...