PHP接收不到WebSocket消息的根本原因在于未完成握手、未解析掩码帧或连接被中断;需严格实现HTTP/1.1 101响应、SHA1+base64计算Sec-WebSocket-Accept、手动解帧、常驻进程及心跳保活。

WebSocket连接建立后收不到消息?检查 on_message 是否被正确注册
很多初学者用 websocket-client 时,写了回调函数却始终没触发,根本原因是没调用 ws.run_forever() 或漏掉了 ws.on_message = your_handler 这类绑定。该库不支持装饰器式注册(如 @ws.on_message),必须显式赋值。
常见错误现象:Connection opened 日志出现,但后续无任何 on_message 输出,控制台也无报错。
- 确保在
connect()后、run_forever()前完成所有ws.on_*属性赋值 - 不要在
on_open回调里才赋值on_message—— WebSocket 可能在回调执行前就已收到首条消息 - 若使用多线程或异步场景,注意
on_message默认运行在子线程中,避免直接操作主线程的 GUI 或未加锁的共享变量
抓到的 WebSocket 数据是乱码或二进制?优先检查 opcode 和解码逻辑
即时通讯协议(如微信 Web 版、钉钉网页端)普遍用 OPCODE_BINARY 发送加密或序列化后的 payload,直接 .decode('utf-8') 会抛 UnicodeDecodeError。不能默认当作文本处理。
使用场景:监听到数据但 msg 是 bytes 类型,len(msg) 明显大于预期,或包含大量不可见字符。
立即学习“Python免费学习笔记(深入)”;
- 在
on_message中先打印type(msg)和msg[:32]判断原始类型 - 若为
bytes,尝试用json.loads(msg.decode('utf-8', errors='ignore'))容错解码,再观察字段结构 - 部分协议(如早期企业微信)用 Protobuf 序列化,此时需逆向获取 .proto 文件并用
protoc生成 Python 类,否则无法解析
为什么能连上但很快被断开?服务端心跳与客户端保活必须同步
绝大多数 IM 服务端要求客户端定期发送 ping/pong 帧,超时(通常 30–60 秒)无响应即主动断连。而 websocket-client 默认不自动处理心跳,依赖用户手动实现。
错误现象:on_close 被频繁触发,日志显示 Connection is already closed 或 socket.timeout;抓包可见服务端发了 PING 帧但客户端未回 PONG。
- 启用内置心跳:初始化时传参
websocket.WebSocketApp(..., keep_running=True, on_ping=..., on_pong=...),但注意keep_running=True并非万能,某些服务端只认特定格式的 pong - 更可靠的做法:用
threading.Timer每 25 秒调用一次ws.send('', websocket.ABNF.OPCODE_PING) - 避免在
on_message中做耗时操作(如写文件、发 HTTP 请求),否则可能错过心跳窗口
绕过前端加密逻辑才能解密消息?别硬刚 JS,优先查 __wxjs_environment 和 localStorage
微信 Web 版等应用在建立 WebSocket 前,会用前端 JS 对消息体做 AES 或 RSA 加密。单纯监听 WebSocket 流只能拿到密文。想解密,得复现密钥生成逻辑——而这往往依赖浏览器环境变量。
容易被忽略的点:密钥不是固定字符串,而是从 localStorage、window 对象或初始化脚本中动态读取的。
- 打开开发者工具,在 Application → Storage → LocalStorage 中搜索
key、sid、uin等字段,常含加密所需 salt 或 iv - 在 Sources 面板全局搜索
AES、CryptoJS、encrypt,定位密钥派生函数,关注其参数是否来自window.__wxjs_environment或document.cookie - 若加密逻辑极复杂(如微信的 multi-layer obfuscation),建议改用 Puppeteer / Playwright 注入 JS 直接调用页面已有加密函数,比纯 Python 复现更稳
真实环境里,WebSocket 抓包只是第一步;密钥来源、协议分帧、服务端反爬策略(如校验 User-Agent + Referer + WebSocket subprotocol)这些环节缺一不可。漏掉任意一个,看到的都只是加密噪音。


















