JMeter发送的WebSocket socket.io请求无法被NestJS服务接收
问题现象
- 部署的NestJS服务,使用如下socket.io-client代码可正常发起连接,服务端可成功接收连接:
const io = require('socket.io-client'); const socket = io("http://localhost:3000", { transports: ["websocket"], pingTimeout: 10000, pingInterval: 25000, }); const someDelay = 10; socket.on('connect', function () { console.log('connected...'); })
- 上述客户端连接成功后会触发回调打印
connected...日志,连接流程无异常。 - 使用JMeter发起相同配置的WebSocket连接请求时,请求始终无法到达服务端;连接建立后携带token发起的后续请求同样无法被服务端接收,整个流程无显式报错但请求始终未触达服务端,请求响应采样器返回报错信息。
根因分析
核心原因是JMeter自带的通用WebSocket采样器仅支持标准原生WebSocket协议,不兼容Socket.IO的私有协议封装:
Socket.IO并非原生WebSocket实现,即便指定transports: ["websocket"]仅走WebSocket传输,它仍在标准协议之上做了私有封装,包括固定握手路径、协议版本校验、连接确认帧规则、消息帧格式约定、心跳逻辑等,直接用原生WebSocket采样器裸连,不符合Socket.IO的协议校验规则,请求会被服务端直接丢弃,不会进入业务逻辑层。
解决方案
方案1(推荐):使用Socket.IO专用JMeter插件
通过JMeter插件管理器搜索安装Socket.io Plugin,该插件原生适配Socket.IO协议逻辑,无需手动处理协议封装:
- 配置时直接填写和JS客户端一致的服务地址
http://localhost:3000 - 传输协议选择websocket,同步配置和JS客户端一致的
pingTimeout、pingInterval参数 - 后续发送携带token的事件请求时,直接填写事件名和对应JSON载荷即可,插件会自动按Socket.IO协议格式封装帧内容
方案2:使用原生WebSocket采样器手动适配Socket.IO协议
如果不想安装额外插件,可按Socket.IO协议规则手动配置原生采样器的交互逻辑:
- 修正连接地址:不要直接连
ws://localhost:3000,需使用Socket.IO固定握手地址格式:ws://localhost:3000/socket.io/?EIO=4&transport=websocket,其中EIO=4对应Socket.IO v4版本协议标识,需和服务端实际使用的Socket.IO主版本匹配;如果NestJS网关配置了自定义命名空间,地址中要补充命名空间路径。 - 完成握手确认:连接建立后,服务端会第一时间返回格式为
0{"sid":"<会话ID>","pingInterval":25000,"pingTimeout":10000}的握手响应帧,收到该帧后必须立刻向服务端发送内容为40的文本帧,完成连接确认,否则服务端不会将该连接标记为有效连接,后续所有消息都会被丢弃。 - 按协议格式封装消息:后续发送业务请求(比如携带token的认证请求)时,不能直接发送原始JSON内容,需按照Socket.IO消息帧规则封装,格式为
42["<事件名>",{token:"<你的token值>"}],其中42是Socket.IO普通事件帧的固定前缀,后面紧跟数组格式的内容,第一个元素是事件名称,第二个元素是事件携带的载荷数据。 - 保活配置:按照和客户端一致的间隔发送心跳帧(内容为
2),收到服务端发送的3帧时无需额外处理,避免连接因心跳超时被服务端断开。
内容的提问来源于stack exchange,提问作者Asif Saeed
相关产品推荐
相关产品推荐

