webman不能直接处理modbus tcp流量,因其仅支持http/https/websocket协议;modbus tcp需通过独立workerman tcp worker解析,webman仅提供http接口并与之通过redis等通信。

Webman 不能直接处理 Modbus TCP 流量
Webman 是基于 Workerman 的 Web 框架,它默认只处理 HTTP/HTTPS 和 WebSocket 协议;Modbus TCP 是裸 TCP 协议,没有 HTTP 头、不走路由、不触发控制器,Webman 的中间件、路由、Request 对象对它完全不可见。直接在 Webman 里写 $request->get('function_code') 或用 @Controller 注解监听 502 端口,必然失败。
常见错误现象:
- 启动时报错
Failed to bind address 0.0.0.0:502: Permission denied(Linux 下非 root 不允许绑定 1024 以下端口) - 即使改用高权限启动,
onMessage收到的也是乱序字节流,无法识别事务 ID 或功能码 - 试图用 Webman 的
HttpServer监听tcp://0.0.0.0:502,结果 Worker 进程直接退出——协议类不匹配
正确做法:Workerman 原生 TCP Worker + Webman HTTP API 双轨并行
必须拆开职责:Workerman 负责原始 Modbus TCP 连接管理与协议解析,Webman 仅提供配置界面、状态查询、告警推送等 HTTP 接口。两者通过本地进程通信桥接,而非共用一个 Worker 实例。
实操建议:
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 新建
modbus_worker.php,用Worker('tcp://0.0.0.0:502')启动独立 Worker,实现连接池、心跳、CRC 校验、寄存器缓存 - Webman 后端(如
app/controller/ModbusController.php)不碰 socket,只读写共享内存或 Redis 中的设备快照:redis->hGetAll('modbus:device:PLC_01') - Modbus Worker 解析完一帧后,用
redis->publish('modbus.update', json_encode([...]))通知 Webman 更新缓存,避免轮询 - 若需下发指令(如写保持寄存器),Webman 接收 HTTP POST 后,发消息到 Redis channel 或 Unix Socket,由 Modbus Worker 拦截并构造
\x00\x02\x00\x00\x00\x06\x01\x06\x00\x00\x00\x01发往 PLC
Modbus TCP 帧解析必须自己写,别信“自动解包”
Workerman 的 TcpConnection onMessage 回调只返回内核 recv() 的原始字节,不是完整 Modbus 报文。工业现场的 Modbus TCP 流存在粘包、断帧、跨包 CRC 错位等问题,靠 substr($data, 7) 硬切会丢数据。
关键点:
- 每个
$connection必须绑定私有缓冲区:$connection->modbus_buffer ??= '',拼接所有收到的$data - Modbus TCP 报文头固定 7 字节(事务 ID + 协议 ID + 长度 + 单元 ID),先检查
strlen($buf) >= 7才尝试提取长度字段:unpack('n', substr($buf, 4, 2))[1] - 总长度 = 6 + payload_len,只有
strlen($buf) >= $total_len才截取完整帧,否则继续等待 - 校验和不在 Modbus TCP 中(那是 RTU 的事),但要验证功能码是否为
0x03(读保持寄存器)或0x10(写多个寄存器),非法码直接丢弃 - 超时清理不可少:在
onConnect里用Timer::add(30, function() use ($connection) { $connection->modbus_buffer = ''; })
Nginx 反代 Webman 时,千万别把 Modbus 流量也引过去
Webman 项目常配 Nginx 做 HTTPS 终结和静态资源服务,但 Nginx 默认不转发 TCP 流量。如果误把 proxy_pass http://127.0.0.1:502 加进 location,Nginx 会尝试用 HTTP 协议跟 Modbus Worker 通信,结果返回 502 Bad Gateway 或空响应。
正确隔离方式:
- Webman HTTP 流量走
http://127.0.0.1:8080(Webman 默认端口),Nginxlocation /代理此处 - Modbus TCP 流量走
tcp://127.0.0.1:502,**不经过 Nginx**,前端看板 JS 也不该直连该端口(浏览器不支持 raw TCP) - WebSocket 推送用
wss://your-domain.com/ws(Nginx 反代到 Webman 的 WS Worker),数据来源是 Modbus Worker 写入 Redis 的变更事件 - 调试时用
telnet 127.0.0.1 502或nc -v 127.0.0.1 502验证 Modbus Worker 是否存活,别依赖浏览器 DevTools










