Socket.io 1.x: 只使用 WebSockets?
Socket.io 1.x: use WebSockets only?
我们正在开发一个 Web 应用程序,出于各种原因,该应用程序 运行 仅在现代浏览器 (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
没有任何区别。这正常吗?
socket.io 有两种类型的 "upgrades"。首先(在 socket.io 1.0+ 中),socket.io 使用 http 轮询请求启动所有连接,它实际上可能仅与 http 请求交换一些初始数据。然后,在那之后的某个时刻,它会尝试实际启动一个 webSocket 连接。 webSocket 连接是通过发送指定 upgrade: websocket
header 的特定类型的 http 请求来完成的,然后服务器可以适当地响应它是否支持 websocket。如果服务器同意升级,那么特定的 http 连接是 "upgraded" 到 webSocket 协议。此时,客户端知道支持 webSocket 并停止使用轮询 http 请求,从而完成其 upgrade
到 webSocket。
您可以通过在客户端执行此操作来完全阻止初始 http 轮询:
var socket = io({transports: ['websocket'], upgrade: false});
这将阻止来自您自己的合作客户端的轮询连接。如果你想阻止任何客户端使用轮询,那么你可以将它添加到服务器:
io.set('transports', ['websocket']);
但是,如果您在服务器上设置此项,socket.io 最初使用 http 轮询连接的客户端将根本无法工作。因此,这应该只与客户端中的正确设置相匹配,这样客户端就不会以轮询开始。
这将告诉两端您只想使用 webSockets,并且 socket.io 将跳过开始时额外的 http 轮询。公平警告,这样做需要 webSocket 支持,因此这排除了与尚不支持 webSocket 的旧版本 IE 的兼容性。如果您想保持兼容性,那么只需让 socket.io 最初使用几个 http 请求即可。
这里有更多关于协议从 http 升级到 webSocket 的信息。
webSockets 协议使用 HTTP 连接启动每个 webSocket。这就是所有 webSocket 的工作方式。该 HTTP 连接包含一些 header 指示浏览器将 "like" 升级到 webSockets 协议。如果服务器支持该协议,那么它会响应告诉客户端它将升级到 webSocket 协议,然后那个套接字从 HTTP 协议切换到 webSocket 协议。这就是 webSocket 连接设计的工作方式。因此,您看到您的 webSocket 连接以 HTTP 连接开始这一事实是 100% 正常的。
您可以将 socket.io 配置为从不使用长轮询,如果这会让您感觉更好的话,但这不会改变 webSocket 连接仍将以 HTTP 连接开始,然后升级到 webSocket 的事实协议,它不会提高支持 webSockets 的现代浏览器的运行效率。但是,它将使您的连接无法在旧版浏览器中工作。
我想我应该添加到上面接受的答案中,就好像有人想消除 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
如果您不强制传输,您会看到 "polling" 而不是 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,我相信这是最初的问题。我希望这可以帮助那里的人...
要告诉 Socket.IO 仅使用 WebSocket 而不是首先使用一些 XHR 请求,只需将其添加到节点服务器:
io.set('transports', ['websocket']);
然后在客户端添加:
var socket = io({transports: ['websocket']});
这告诉 Socket.IO 只使用 WebSocket 协议,别无其他;它更干净、更快,并且在客户端和服务器端使用的资源更少。
现在您只会在网络请求列表中看到一个 WebSocket 连接,请记住 IE9 及更早版本不能使用 WebSocket。
我发布该答案是因为接受的答案不正确 - 它混淆了从长轮询 AJAX 到 WebSocket 的 Socket.IO 升级与 WSS 协议 "Connection: Upgrade" 请求。问题不在于 WebSocket 连接从 HTTP 开始并升级到 WebSocket——怎么可能不呢? - 但是 Socket.IO 以长轮询 AJAX 连接开始,即使在支持 WebSocket 的浏览器上也是如此,并且仅在交换一些流量后才对其进行升级。在火狐或者Chrome.
的开发者工具中很容易看到
题主的观察是正确的。
Socket.IO 中的 "upgrade" 并不是指通常被误解的 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 秒
来自这 2 个请求:
- HTML 页面本身
- 连接升级到 WebSocket
(连接升级请求在开发者工具上可见,响应为 101 Switching Protocols。)
Socket.IO
6 个请求,181.56 KB,0.25 秒
来自这 6 个请求:
- HTML 页面本身
- Socket.IO 的 JavaScript(180 KB)
- 第一次长轮询AJAX请求
- 秒长轮询AJAX请求
- 第三次长轮询AJAX请求
- 连接升级到 WebSocket
详情
我在本地主机上获得的 WebSocket 结果:
Socket.IO 我在本地主机上得到的结果:
测试你自己
我发布了代码on npm and 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。
我们正在开发一个 Web 应用程序,出于各种原因,该应用程序 运行 仅在现代浏览器 (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
没有任何区别。这正常吗?
socket.io 有两种类型的 "upgrades"。首先(在 socket.io 1.0+ 中),socket.io 使用 http 轮询请求启动所有连接,它实际上可能仅与 http 请求交换一些初始数据。然后,在那之后的某个时刻,它会尝试实际启动一个 webSocket 连接。 webSocket 连接是通过发送指定 upgrade: websocket
header 的特定类型的 http 请求来完成的,然后服务器可以适当地响应它是否支持 websocket。如果服务器同意升级,那么特定的 http 连接是 "upgraded" 到 webSocket 协议。此时,客户端知道支持 webSocket 并停止使用轮询 http 请求,从而完成其 upgrade
到 webSocket。
您可以通过在客户端执行此操作来完全阻止初始 http 轮询:
var socket = io({transports: ['websocket'], upgrade: false});
这将阻止来自您自己的合作客户端的轮询连接。如果你想阻止任何客户端使用轮询,那么你可以将它添加到服务器:
io.set('transports', ['websocket']);
但是,如果您在服务器上设置此项,socket.io 最初使用 http 轮询连接的客户端将根本无法工作。因此,这应该只与客户端中的正确设置相匹配,这样客户端就不会以轮询开始。
这将告诉两端您只想使用 webSockets,并且 socket.io 将跳过开始时额外的 http 轮询。公平警告,这样做需要 webSocket 支持,因此这排除了与尚不支持 webSocket 的旧版本 IE 的兼容性。如果您想保持兼容性,那么只需让 socket.io 最初使用几个 http 请求即可。
这里有更多关于协议从 http 升级到 webSocket 的信息。
webSockets 协议使用 HTTP 连接启动每个 webSocket。这就是所有 webSocket 的工作方式。该 HTTP 连接包含一些 header 指示浏览器将 "like" 升级到 webSockets 协议。如果服务器支持该协议,那么它会响应告诉客户端它将升级到 webSocket 协议,然后那个套接字从 HTTP 协议切换到 webSocket 协议。这就是 webSocket 连接设计的工作方式。因此,您看到您的 webSocket 连接以 HTTP 连接开始这一事实是 100% 正常的。
您可以将 socket.io 配置为从不使用长轮询,如果这会让您感觉更好的话,但这不会改变 webSocket 连接仍将以 HTTP 连接开始,然后升级到 webSocket 的事实协议,它不会提高支持 webSockets 的现代浏览器的运行效率。但是,它将使您的连接无法在旧版浏览器中工作。
我想我应该添加到上面接受的答案中,就好像有人想消除 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
如果您不强制传输,您会看到 "polling" 而不是 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,我相信这是最初的问题。我希望这可以帮助那里的人...
要告诉 Socket.IO 仅使用 WebSocket 而不是首先使用一些 XHR 请求,只需将其添加到节点服务器:
io.set('transports', ['websocket']);
然后在客户端添加:
var socket = io({transports: ['websocket']});
这告诉 Socket.IO 只使用 WebSocket 协议,别无其他;它更干净、更快,并且在客户端和服务器端使用的资源更少。
现在您只会在网络请求列表中看到一个 WebSocket 连接,请记住 IE9 及更早版本不能使用 WebSocket。
我发布该答案是因为接受的答案不正确 - 它混淆了从长轮询 AJAX 到 WebSocket 的 Socket.IO 升级与 WSS 协议 "Connection: Upgrade" 请求。问题不在于 WebSocket 连接从 HTTP 开始并升级到 WebSocket——怎么可能不呢? - 但是 Socket.IO 以长轮询 AJAX 连接开始,即使在支持 WebSocket 的浏览器上也是如此,并且仅在交换一些流量后才对其进行升级。在火狐或者Chrome.
的开发者工具中很容易看到题主的观察是正确的。 Socket.IO 中的 "upgrade" 并不是指通常被误解的 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 秒
来自这 2 个请求:
- HTML 页面本身
- 连接升级到 WebSocket
(连接升级请求在开发者工具上可见,响应为 101 Switching Protocols。)
Socket.IO
6 个请求,181.56 KB,0.25 秒
来自这 6 个请求:
- HTML 页面本身
- Socket.IO 的 JavaScript(180 KB)
- 第一次长轮询AJAX请求
- 秒长轮询AJAX请求
- 第三次长轮询AJAX请求
- 连接升级到 WebSocket
详情
我在本地主机上获得的 WebSocket 结果:
Socket.IO 我在本地主机上得到的结果:
测试你自己
我发布了代码on npm and 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。