Socket.io双向通信教程,如何修改底层协议信息
发布于 作者:苏南大叔 来源:程序如此灵动~
众所知之的是:socket.io
并不是一个协议的名词,它是一个兼容性质的框架。它的底层协议,不仅包括websocket
,还包括其它"polling"/"flashsocket"/"xhr-polling"/"jsonp-polling"/"iframe streaming"等其它技术手段。在最新的v4
版socket.io
中,transports
的默认值有两个,分别是:websocket
和polling
。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10
,nodejs@20.18.0
,express@4.21.2
,socket.io@4.8.1
。本文讲解socket.io
的底层协议切换相关事项。
前文回顾
socket.io
的基础使用例子:
socket.io
的路径相关设置:
服务端基础代码:
//...
const socketio = require("socket.io");
const io = socketio(expressServer,{
serveClient: false, // 是否控制客户端文件
path:"/.io/", // 客户端所有请求路径,注意和客户端设定保持一致
});
//...
客户端基础代码:
<script>
const socket = io(
"https://localhost:82", // 一定概率下,会出现跨域问题
{ path:'/.io/' } // 注意和服务端的path设定保持一致
);
// ...
</script>
参数 transports
注意这个单词的拼写是复数带s
,和上一篇文章里面的path
参数,放在同一个位置。主要表明支持什么样的底层协议。所以,客户端设置也可以,服务端设置也可以。两者是协商的关系,当然如果协商不一致,就以客户端的为“错误”请求为主么,毕竟还是客户端占有主动性。
服务端和客户端,两者【协商】的结果。协商的标准很多,并不仅仅是简单的取transports
的交集,就可以强制使用某个底层协议,还存在着其它的因素。但是,如果双方都设置成仅仅支持某一种协议。那么,大概率情况下,协商的结果就是它了。
设置为只polling
轮询实际上是以http
协议为主的,客户端定期向服务端发起请求,以检测是否存在消息。
服务端:
const socketio = require("socket.io");
const io = socketio(expressServer,{
transports: ['polling'] // 只允许轮询
});
最好同时设置客户端:
const socket = io({
transports:["polling"], // 只允许轮询
// upgrade: false,
});
设置为只 websocket
目前来说,默认大多数情况下,都是会使用websocket
的。但是,也可以在服务器端或者客户端设置为只支持websocket
。
服务端:
const socketio = require("socket.io");
const io = socketio(expressServer,{
transports: ['websocket'] // 只允许websocket
});
最好同时设置客户端:
const socket = io({
transports:["websocket"] // 只允许websocket
});
默认参数
如果保持transports
参数默认的话,也可能会出现
四个(有且仅有)看起来非常“轮询”的请求,如下图所示。并且部分服务器端消息,就可能由polling
请求传递回来的。如下图所示:
包括其中的websocket
的一个请求,一共五个请求。它们的作用分别是:
Engine.IO
握手(包含会话ID
— 此处zBjrh...AAAK
— 用于后续请求)tSocket.IO
握手请求(包含auth
选项的值)Socket.IO
握手响应(包含Socket#id
)WebSocket
连接- 第一个
HTTP
长轮询请求,一旦建立WebSocket
连接就关闭。
代码判断当前协议
其实从f12
抓包的结果上,很容易就能判断当前的基础底层协议是啥。但是如果代码有办法进行判断的话,也是不错的选择。对吧。
服务端判断代码:
const io = socketio(expressServer, {});
io.on("connection", (socket) => {
const transport = socket.conn.transport.name; // in most cases, "polling"
console.log(transport);
socket.conn.on("upgrade", () => {
const upgradedTransport = socket.conn.transport.name; // in most cases, "websocket"
console.log("升级为:",upgradedTransport);
});
//...
});
客户端判断代码:
const socket = io({});
socket.on("connect", () => {
const transport = socket.io.engine.transport.name; // 在大多数情况下, "polling"
console.log(transport);
socket.io.engine.on("upgrade", () => {
const upgradedTransport = socket.io.engine.transport.name; // 在大多数情况下, "websocket"
console.log("升级为:",upgradedTransport);
});
});
//...
题外话
所以,如果想抓别人的socket.io
数据的话,最好的办法就是:自己的代码仅仅支持websocket
!然后直接连对方的ws://
或者wss://
接口。直接减少复杂度!
结论
结论就是:服务端和客户端都可以通过transports
参数,声明自己仅支持的底层协议,以影响最终的协商结果。不管协商结果如何,开始的这几个非常类似polling
请求的握手请求,也是不可避免的。


