siocat:像 wscat 一样调试 Socket.IO
如果你写过 Node.js 实时应用,大概率用过 wscat 调试 WebSocket。但如果服务端用的是 Socket.IO,wscat 就只能看到 40、42["event",data] 这样的 Engine.IO 封包,既不能直接发 Socket.IO 事件,也搞不定命名空间、ACK 回调和握手认证。
siocat 就是为这个问题做的:一个类似 wscat 的 Socket.IO 终端客户端,默认是一个带消息流、事件列表和命令菜单的 TUI,也可以退化成纯命令行模式。它支持 Socket.IO v2、v3、v4,能自动捕获服务端推来的任意事件,输入载荷还支持 JSON5。
一、为什么不用 wscat
Socket.IO 并不是标准 WebSocket。它在 WebSocket 之上又封了一层自己的协议:
- Engine.IO 握手:连接建立前有
EIO=3/EIO=4这样的协议版本握手,还有 ping / pong 心跳。 - Socket.IO 封包:
42["event", data]、43["event", data, ackId]这类封包,事件名、参数、ACK id 都是协议的一部分。 - 命名空间与 ACK:
namespace、auth、事件回调、二进制附件,这些都不是裸 WebSocket 的概念。 - 版本差异:Socket.IO 2.x / 1.x 用的是 Engine.IO v3,3.x / 4.x 用的是 Engine.IO v4,握手参数和封包编码都不一样。
所以调试 Socket.IO 时,真正需要的是一个“懂 Socket.IO 协议栈”的终端客户端,而不是一个只会收发 WebSocket 文本的小工具。
二、siocat 是什么
一句话概括:siocat = wscat 的交互方式 + Socket.IO 协议栈 + TUI 仪表盘。
它有两种运行模式:
- TUI 模式(默认):一个双栏仪表盘,左边是实时消息流,右边是已捕获事件列表和会话检查器,底部是可编辑的命令输入框。
- CLI 模式(
--no-tui):一个带彩色输出的交互式 Readline 会话,适合脚本、CI 或者不喜欢全屏 TUI 的场景。
1 | |
项目目前的结构很轻:
1 | |
三、快速开始
项目还没有发布到 npm,直接 clone 下来本地跑即可。要求 Node.js >= 16。
1 | |
几个常用连接示例:
1 | |
四、TUI:一个仪表盘,而不只是一个输入框
TUI 是 siocat 最像“现代终端应用”的部分。它没有把所有命令都塞进帮助文本,而是在界面上留了足够的可视化反馈。
1. 实时消息流
所有上下行事件都会进入消息流,带毫秒级时间戳和状态徽标:
▼ IN:服务端推来的事件▲ OUT:客户端发出的事件✓ ACK:服务端返回的 ACK* SYS:连接、断开、重连、传输升级等系统消息✖ ERR:连接或发送错误
长文本会自动换行,鼠标滚轮可以滚动查看历史消息。
2. 已捕获事件列表
服务端推来了哪些事件,右侧列表会直接列出来。选中一个事件后按 Enter,或者用鼠标点击,就能以这个事件名预填一个 emit 表单,不用自己手敲事件名。
这个能力来自 Socket.IO 客户端的 wildcard / catch-all 机制:v3 / v4 用 onAny / onAnyOutgoing,v2 则通过 onevent 和 emit 做兼容 hook。所以不需要预先注册监听,服务端推什么都能看到。
3. 会话检查器
TUI 会实时显示当前连接的关键信息:
- 连接状态(Connected / Disconnected)
- 传输协议(websocket / polling)
- Socket ID
- 运行时长
- 收包 / 发包数量
- ACK 次数
排查“到底是连上了没有”“现在是走 WebSocket 还是轮询”这类问题时,不用再去翻日志。
4. 命令菜单与行编辑器
底部输入框集成了一组常用命令,按数字键 1 ~ 7 就能唤起对应表单:emit、ack、filter、stats、help 等。行编辑器本身支持:
←/→、Home/End、鼠标点击定位光标Ctrl+A/Ctrl+E跳到行首 / 行尾Ctrl+U/Ctrl+K删除光标前 / 后的内容Ctrl+W删除前一个单词↑/↓翻阅历史命令- 实时语法高亮:命令名、事件名、键名、字符串、数字、布尔值颜色都不同
几个常用快捷键:
| 快捷键 | 功能 |
|---|---|
Tab | 在命令菜单、输入框、消息视窗、事件列表之间循环切换焦点 |
1 ~ 7 | 直接触发命令菜单中的操作 |
Enter(事件列表中) | 用选中事件名快速打开 emit 表单 |
F1 / ? | 打开 / 关闭帮助浮窗 |
F2 | 打开实时过滤器 |
Ctrl+L | 清空消息视窗 |
Ctrl+C / :q | 退出 |
五、交互命令
无论 TUI 还是 CLI 模式,都可以直接输入这些命令:
| 命令 | 说明 |
|---|---|
emit / send <event> [payload] | 发送一个事件 |
ack <event> [payload] | 发送事件并等待服务端 ACK 回调 |
filter [pattern] | 按正则或关键字过滤消息流,留空清除 |
stats / info | 查看连接详情和事件统计 |
clear / cls | 清空消息记录 |
help / ? | 查看帮助 |
exit / quit / :q | 断开连接并退出 |
示例:
1 | |
六、宽松载荷输入
调试实时服务时,载荷格式往往很琐碎。siocat 在输入侧做了几层兼容:
- JSON5:键名可以不加引号,也支持单引号字符串,写起来更像“草稿”。
- 纯字符串:不是合法 JSON5 时,会退化成原始字符串。
- 二进制前缀:
hex:和base64:可以直接发送 Buffer。 - ACK 超时:默认 5000ms,可以用
--ack-timeout调整。
1 | |
ACK 消息里还会计算往返耗时,方便判断服务端处理是不是有延迟。
七、多协议兼容
Socket.IO 的兼容问题是这个项目最实际的部分。服务端版本和客户端 SDK 版本如果对不上,常见现象是“能建立连接但握手失败”或者“客户端一直重连”。
siocat 通过 npm alias 同时装了三个版本的客户端:
1 | |
运行时根据 -p 参数动态加载:
1 | |
选参数时可以按这张表:
| 目标服务端 | 推荐参数 | 说明 |
|---|---|---|
| Socket.IO 4.x | 默认 / -p 4 | Engine.IO v4,Socket.IO protocol v5 |
| Socket.IO 3.x | -p 3 或 -p 4 | 3.x 与 4.x 底层协议一致,通常都能互通 |
| Socket.IO 2.x / 1.x | -p 2 | Engine.IO v3,Socket.IO protocol v4 |
开启 allowEIO3: true 的 4.x | 任意 | 服务端向下兼容旧版握手 |
一个容易踩的坑:-p 指的是 Socket.IO 客户端 SDK 的大版本,不是 Engine.IO 版本。所以看到 EIO=3 / EIO=4 时不要直接照抄,要对应到客户端的 -p 2 或 -p 3/4。
八、实现上值得看的几个点
1. 自动捕获任意事件
v3 / v4 的原生 wildcard API 之外,v2 没有 onAny,所以 siocat 直接 hook 了 onevent 和 emit:
1 | |
这样消息流里看到的是已经解析过的事件名和参数,而不是原始封包。
2. ACK 等待与超时
ack 命令不是简单 emit 一下就算了,它会把 ACK 回调包成一个 Promise,并设置超时:
1 | |
控制台里会同时显示响应内容和往返耗时,排查 ACK 延迟很直观。
3. 兼容 v2 的请求头
Socket.IO v2 客户端没有 extraHeaders,要传自定义 HTTP 头,得走 transportOptions.polling.extraHeaders。siocat 在 connect() 里按版本分支处理:
1 | |
这类“同一件事,两个版本两套 API”的细节,正是 Socket.IO 调试工具真正需要处理的地方。
九、本地测试
项目自带一个同时支持 v2 / v3 / v4 的 mock server,服务端开启了 allowEIO3: true,所以旧版客户端也能连上:
1 | |
然后另开一个终端连接:
1 | |
十、参考资料与延伸阅读
- siocat 项目仓库
- Socket.IO 官方文档
- Socket.IO Protocol 规范
- Engine.IO Protocol 规范
- wscat:WebSocket 命令行客户端
- socket.io-client npm 包
- blessed:终端界面库
- JSON5 语法说明
十一、小结
调试 Socket.IO 时,最常用的动作其实就几个:连接、看事件、发事件、等 ACK、过滤噪音、确认当前传输和协议。siocat 做的事情就是把这些动作收进一个终端界面里,同时补上 Socket.IO 特有的协议细节。
它现在还没发布到 npm,作为本地开发工具使用,npm install 之后 node bin/siocat.js 就能跑。项目在 GitHub,MIT 协议,欢迎 issue 和 PR。
