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

IT技术 javascript websocket socket.io
2021-02-05 23:21:14

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

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

我四处搜索,我只能找到这个 wiki 页面,但它是关于 Socket.io 0.9 的。

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

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

奇怪的是,仅仅将transports属性设置为一个数组websockets是不够的;我也不得不禁用upgrade. 这个对吗?

更新

我有了一些新的发现。

随着transports设置为['websocket']只,它没有任何阉差upgrade被启用。这是正常的吗?

4个回答

socket.io 有两种类型的“升级”。首先(在 socket.io 1.0+ 中),socket.io 用一个 http 轮询请求启动所有连接,它实际上可能只用一个 http 请求交换一些初始数据。然后,在此之后的某个时刻,它会尝试实际启动 webSocket 连接。webSocket 连接是通过发送特定类型的 http 请求来完成的,该请求指定了一个upgrade: websocket标头,然后服务器可以适当地响应它是否支持 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 连接包含一些标头,表明浏览器“想要”升级到 webSockets 协议。如果服务器支持该协议,则它会响应告诉客户端它将升级到 webSocket 协议,并且该套接字然后从 HTTP 协议切换到 webSocket 协议。这就是 webSocket 连接的工作原理。因此,您看到以 HTTP 连接开始的 webSocket 连接这一事实是 100% 正常的。

如果这让您感觉更好,您可以将 socket.io 配置为从不使用长轮询,但这不会改变 webSocket 连接仍以 HTTP 连接开始的事实,然后将其升级为 webSocket 协议,并且不会改善支持 webSockets 的现代浏览器的运行效率。但是,它会使您的连接在旧浏览器中无法正常工作。

另一方面,如果我启用“轮询”,使用 upgrade true 我可以首先看到一些请求,然后是 WSS 请求。使用 upgrade false 时,websocket 永远不会启动。
2021-03-22 23:21:14
@Qualcuno - 由于所有 webSocket 连接都从 HTTP 请求开始,然后“升级”到 webSocket 协议,如果关闭该upgrade选项使其永远不会变成 webSocket 连接,我也不会感到惊讶为了确认这一点,人们必须研究 engine.io 源代码,因为文档并没有真正解释。如果不进一步研究源代码,我不知道确切。如果您想了解 webSocket 连接是如何启动的,这是一个很好的参考
2021-03-26 23:21:14
@Qualcuno 您的观察是正确的。Socket.IO 中的“升级”并不是指通常被误解的 HTTP 到 WSS 协议的升级,而是指 Socket.IO 连接从长轮询 AJAX 连接到 WebSocket 的升级。如果您已经开始使用 WebSocket(这不是默认设置),那么 upgrade false 将不起作用,因为您不需要升级。如果您从轮询开始并禁用升级,那么它会保持这种状态并且不会升级到 WebSocket。我的回答对细节
2021-03-27 23:21:14
其实,还有更奇怪的。通过仅将传输设置为 websocket,它不会对 Inspector 升级是真还是假产生任何影响。在这两种情况下,我只看到一个请求,上面的那个(它总是一个 HTTP 请求,是的,但是是在 wss:// 协议上提出的)
2021-04-02 23:21:14
谢谢解释。那么,禁用有upgrade什么作用呢?通过禁用它,我在 Chrome 检查器上看到了一个 GET 请求,wss://.../socket.io/?EIO=3&transport=websocket并且响应状态代码是 101(切换协议)。该请求始终处于“待处理”状态,我可以看到客户端和服务器交换的帧。
2021-04-03 23:21:14

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

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

在客户端添加这个:

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

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

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

你建议使用这个吗?
2021-04-04 23:21:14
我个人使用这个,是的,我建议任何不关心 IE9 或更旧浏览器的人也使用它,即如果你生活在现代,在 2017 年,是的,你应该避免 XHR 回退,只使用 websocket运输。IE9 = < 0.01% 的浏览器,如果您支持它们,请记住它们会使用您的产品 ;)
2021-04-11 23:21:14
是的,我已经在我的网站上实现了这个。工作很棒。我想知道为什么在控制台上有很多对 socketio 的请求,您的解决方案修复了它。是的,我不在乎 0.01% :) 因为我不认识任何人仍在使用 IE,除了我的祖父。
2021-04-13 23:21:14

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

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

如果您想避免从长轮询开始,请参阅arnoldNick Steele 的回答我将更详细地解释正在发生的事情。

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

网络套接字

2 个请求,1.50 KB,0.05 秒

从这两个请求中:

  1. HTML 页面本身
  2. 连接升级到 WebSocket

(连接升级请求在带有 101 Switching Protocols 响应的开发者工具上可见。)

套接字接口

6 个请求,181.56 KB,0.25 秒

从这 6 个请求中:

  1. HTML 页面本身
  2. Socket.IO 的 JavaScript(180 KB)
  3. 第一个长轮询 AJAX 请求
  4. 第二个长轮询 AJAX 请求
  5. 第三个长轮询 AJAX 请求
  6. 连接升级到 WebSocket

细节

我在本地主机上得到的 WebSocket 结果:

WebSocket 结果 - websocket-vs-socket.io module

我在本地主机上得到的 Socket.IO 结果:

Socket.IO 结果 - websocket-vs-socket.io module

测试自己

在 npmGitHub发布了代码,你可以自己运行:

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

并遵守规定。卸载:

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

有关更多信息,请参阅此答案

在一个地方收集所有数据的工作很棒!谢谢!如果所有的工作都像今生一样彻底,就会有一本手册:)
2021-03-18 23:21:14

我想我应该添加到上面接受的答案中,好像有人想消除 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

这对我来说非常有效,因为我可以控制我拥有的客户端,所以我可以立即强制使用 websocket,我相信这就是最初的问题所要问的。我希望这可以帮助那里的人...

嗨,如果我在服务器上使用 Node.js Express Clustering module,那么这会起作用吗...?
2021-04-01 23:21:14
优秀作品 !谢谢。在我的情况下,我指的 https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js 是能够连接到我刚刚使用的 websocketvar url = 'http://localhost:8080'; var socket = io.connect(url,connectionOptions);并获得我的状态 101 =)
2021-04-05 23:21:14
如果客户端已经禁用 XHR 轮询,那么在服务器上禁用 XHR 轮询有什么意义?
2021-04-10 23:21:14