Socket.io 1.x:只使用 WebSockets?
Posted
技术标签:
【中文标题】Socket.io 1.x:只使用 WebSockets?【英文标题】:Socket.io 1.x: use WebSockets only? 【发布时间】:2015-03-30 02:10:11 【问题描述】:出于不同的原因,我们正在开发一个只能在现代浏览器 (IE10+) 上运行的网络应用程序。
我们实现的功能之一是 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
都没有任何区别。这正常吗?
【问题讨论】:
Google 员工注意事项:如果您遇到相反的问题(仅需要使用 HTTP),您可以使用io.set('transports', ['polling']);
,或者如果您使用的是 NestJS, @WebSocketGateway( transports: ['polling'] )
.
【参考方案1】:
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 连接包含一些标头,表明浏览器“希望”升级到 webSockets 协议。如果服务器支持该协议,那么它会响应告诉客户端它将升级到 webSocket 协议,然后那个套接字就会从 HTTP 协议切换到 webSocket 协议。这就是 webSocket 连接的设计方式。因此,您看到您的 webSocket 连接以 HTTP 连接开始这一事实是 100% 正常的。
您可以将 socket.io 配置为从不使用长轮询,如果这会让您感觉更好,但这不会改变 webSocket 连接仍然以 HTTP 连接开始,然后升级到 webSocket 协议并且它将不能提高支持 webSockets 的现代浏览器的运行效率。但是,它将使您的连接无法在旧版浏览器中运行。
【讨论】:
感谢您的解释。那么,禁用upgrade
有什么作用?通过禁用它,我在 Chrome 检查器上看到向wss://.../socket.io/?EIO=3&transport=websocket
发出 GET 请求,响应状态代码为 101(切换协议)。该请求始终处于“待处理”状态,我可以看到客户端和服务器交换的帧。
其实更诡异。通过仅将传输设置为 websocket,无论升级是真还是假,检查器都没有任何区别。在这两种情况下,我只看到一个请求,即上面的一个(它始终是 HTTP 请求,是的,但在 wss:// 协议上发出)
如果我启用“轮询”,另一方面,如果升级为真,我可以首先看到一些请求,然后是 WSS 请求。升级为 false 时,websocket 永远不会启动。
@Qualcuno - 由于所有 webSocket 连接都以 HTTP 请求开始,然后“升级”到 webSocket 协议,因此如果关闭 upgrade
选项可以让它永远不会变成 webSocket 连接。要确认这一点,必须研究 engine.io 源代码,因为文档并没有真正解释。如果不进一步研究源代码,我并不确切知道。这是good reference,如果您想查看 webSocket 连接是如何启动的。
@Qualcuno 您的观察是正确的。 Socket.IO 中的“升级”并不是指 HTTP 到 WSS 协议的升级,这通常被误解,而是指 Socket.IO 连接从长轮询 AJAX 连接到 WebSocket 的升级。如果您已经从 WebSocket 开始(这不是默认设置),则 upgrade false 无效,因为您不需要升级。如果您从轮询开始并禁用升级,那么它会保持这种状态并且不会升级到 WebSocket。详情见my answer【参考方案2】:
我想我应该添加到上面接受的答案,好像有人想消除 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,我相信这就是最初的问题。我希望这可以帮助那里的人...
【讨论】:
如果客户端已经禁用 XHR 轮询,那么在服务器上禁用 XHR 轮询有什么意义/用途? 出色的工作!谢谢。就我而言,我指的是https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js
,因此为了能够连接到我的websocket,我只使用了var url = 'http://localhost:8080'; var socket = io.connect(url,connectionOptions);
并获得了我的状态101 =)
嗨,如果我在服务器上使用 Node.js Express Clustering 模块,那么这可以工作吗...?【参考方案3】:
要告诉 Socket.IO 只使用 WebSocket 而不是首先使用几个 XHR 请求,只需将其添加到 Node 服务器:
io.set('transports', ['websocket']);
然后在客户端添加这个:
var socket = io(transports: ['websocket']);
这告诉 Socket.IO 只使用 WebSocket 协议而不使用其他协议;它更干净、更快,并且在客户端和服务器端使用的资源更少。
现在您只会在网络请求列表中看到一个 WebSocket 连接,请记住 IE9 及更早版本无法使用 WebSocket。
【讨论】:
你建议使用这个吗? 我个人使用这个,是的,我建议任何不关心 IE9 或更旧浏览器的人也使用它,即如果你生活在现代,在 2017 年,是的,你应该避免 XHR 回退并且只使用 websocket 传输。 IE9 = 是的,我已经在我的网站上实现了这个。效果很棒。我在控制台上想知道为什么有很多对 socketio 的请求,你的解决方案修复了它。是的,我不在乎 0.01% :) 因为我不知道还有谁还在使用 IE,除了我的爷爷。【参考方案4】:我发布该答案是因为接受的答案不正确 - 它混淆了从长轮询 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。
如果您想避免从长轮询开始,请参阅 arnold 和 Nick Steele 的回答。我会更详细地解释发生了什么。
这是我在 my experiments 中使用简单的 WebSocket 和 Socket.IO 应用程序观察到的:
WebSocket
2 个请求,1.50 KB,0.05 秒
来自这两个请求:
-
html 页面本身
连接升级到 WebSocket
(连接升级请求在开发者工具上可见,并带有 101 Switching Protocols 响应。)
Socket.IO
6 个请求,181.56 KB,0.25 秒
来自这 6 个请求:
-
HTML 页面本身
Socket.IO 的 javascript(180 KB)
第一个长轮询 AJAX 请求
第二个长轮询 AJAX 请求
第三次长轮询 AJAX 请求
连接升级到 WebSocket
详情
我在 localhost 上得到的 WebSocket 结果:
我在 localhost 上得到的 Socket.IO 结果:
测试自己
我发布了代码on npm和on 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 了解更多信息。
【讨论】:
在一个地方收集所有数据的工作很棒!谢谢!如果所有的工作都像这辈子一样彻底,就会有一本手册:)以上是关于Socket.io 1.x:只使用 WebSockets?的主要内容,如果未能解决你的问题,请参考以下文章
基于heroku socket.io服务器的heroku空闲连接超时
使用websockets手动连接socket.io 1.x,容量测试