# modbus-tcp-multiplex-proxy **Repository Path**: bingshan-guardian-opensource/modbus-tcp-multiplex-proxy ## Basic Information - **Project Name**: modbus-tcp-multiplex-proxy - **Description**: ModbusTCP多路复用代理 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-08 - **Last Updated**: 2026-01-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Modbus TCP 多路复用代理 (Modbus TCP Multiplex Proxy) [English](#english) | [中文](#chinese) --- ## 🎯 项目简介 一个高性能的 Modbus TCP 网关代理程序,解决工业设备不支持多客户端并发访问的问题。 **核心功能:** - ✅ 单一真实从站 → 支持多个客户端并发访问 - ✅ 定期轮询缓存数据,响应速度 < 1ms - ✅ 支持读写透传,写入实时同步 - ✅ 使用成熟开源库,协议标准可靠 - ✅ 跨平台支持 (Linux/Windows/ARM) ## 📦 技术栈 - **语言**: Go 1.21+ - **Modbus主站**: [github.com/goburrow/modbus](https://github.com/goburrow/modbus) - **Modbus从站**: [github.com/tbrandon/mbserver](https://github.com/tbrandon/mbserver) ## 🏗️ 工作原理 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Modbus TCP 多路复用代理 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 真实从站设备 主站轮询模块 从站服务模块 │ │ (PLC/仪表等) (goburrow/modbus) (mbserver) │ │ ↓ ↓ ↓ │ │ 192.168.1.100:502 每200ms读取一次 监听 0.0.0.0:1502 │ │ ↓ ↓ ↓ │ │ 只支持单客户端 → [内存缓存] → 多客户端并发访问 │ │ ↓ ↓ │ │ 每50ms同步到 客户端1 │ │ mbserver寄存器 客户端2 │ │ 客户端3 │ │ ... │ │ 客户端N │ └─────────────────────────────────────────────────────────────────┘ 读取流程: 真实从站 → 定时轮询 → 内存缓存 → 同步到寄存器 → 客户端快速读取 写入流程: 客户端写入 → 透传到真实从站 → 立即更新缓存 → 其他客户端可读 ``` ## 📋 支持的功能码 | 功能码 | 名称 | 支持 | 说明 | |--------|------|------|------| | 0x03 | Read Holding Registers | ✅ | 读保持寄存器(可读写) | | 0x04 | Read Input Registers | ✅ | 读输入寄存器(只读) | | 0x06 | Write Single Register | ✅ | 写单个寄存器(透传) | | 0x10 | Write Multiple Registers | ✅ | 写多个寄存器(透传) | ## 🚀 快速开始 ### 方式一: 下载预编译版本(推荐) 1. 从 [Releases](../../releases) 下载对应平台的可执行文件 2. 解压到目标目录 3. 修改 `config.json` 配置文件 4. 运行程序 ```bash # Linux chmod +x modbus_tcp_multiplex_proxy_linux_amd64_* ./modbus_tcp_multiplex_proxy_linux_amd64_* # Windows modbus_tcp_multiplex_proxy_windows_amd64_*.exe ``` ### 方式二: 从源码构建 #### Windows 构建 ```powershell # 安装依赖 go mod download # 运行构建脚本 .\build.ps1 # 输出在 build 目录 ``` #### Linux/Mac 构建 ```bash # 安装依赖 go mod download # 单平台构建 go build -o modbus_tcp_multiplex_proxy main.go # 多平台构建 GOOS=linux GOARCH=amd64 go build -o modbus_proxy_linux_amd64 main.go GOOS=linux GOARCH=arm64 go build -o modbus_proxy_linux_arm64 main.go GOOS=windows GOARCH=amd64 go build -o modbus_proxy_windows_amd64.exe main.go ``` ## ⚙️ 配置说明 ### 配置文件: `config.json` ```json { "master": { "slave_ip": "192.168.1.100", // 真实从站IP地址 "slave_port": "502", // 真实从站端口(字符串格式) "slave_id": 1, // 真实从站单元ID "start_address": 0, // 读取起始地址 "quantity": 100, // 读取寄存器数量 "poll_interval": 200, // 轮询间隔(毫秒) 建议100-500ms "timeout": 5, // 超时时间(秒) "reconnect_time": 5, // 断线重连等待时间(秒) "function_code": 3 // 读取功能码: 3=保持寄存器, 4=输入寄存器 }, "slave": { "listen_address": "0.0.0.0:502", // 监听地址 (IP:端口格式) "slave_id": 1, // 从站单元ID "enable_write": true // 是否允许写入 } } ``` ### 配置参数详解 #### Master 配置 (主站 - 连接真实设备) | 参数 | 类型 | 必填 | 说明 | 示例 | |------|------|------|------|------| | `slave_ip` | string | ✅ | 真实从站IP地址 | "192.168.1.100" | | `slave_port` | string | ✅ | 真实从站端口(字符串) | "502" | | `slave_id` | byte | ✅ | 真实从站单元ID | 1 | | `start_address` | uint16 | ✅ | 读取起始地址 | 0 | | `quantity` | uint16 | ✅ | 读取寄存器数量(1-125) | 100 | | `poll_interval` | int | ✅ | 轮询间隔(毫秒) | 200 | | `timeout` | int | ✅ | 读取超时时间(秒) | 5 | | `reconnect_time` | int | ✅ | 断线重连等待(秒) | 5 | | `function_code` | byte | ✅ | 读取功能码(3或4) | 3 | **功能码说明:** - `3` = 读保持寄存器 (Read Holding Registers) - 可读可写的寄存器 - `4` = 读输入寄存器 (Read Input Registers) - 只读寄存器,通常用于传感器数据 #### Slave 配置 (从站 - 对外服务) | 参数 | 类型 | 必填 | 说明 | 示例 | |------|------|------|------|------| | `listen_address` | string | ✅ | 监听地址(IP:端口) | "0.0.0.0:502" | | `slave_id` | byte | ✅ | 从站单元ID | 1 | | `enable_write` | bool | ✅ | 是否允许写入 | true | **监听地址说明:** - `"0.0.0.0:502"` - 监听所有网卡,端口502 - `"192.168.1.10:502"` - 仅监听指定网卡 - `"0.0.0.0:1502"` - 使用非标准端口(无需root权限) **写入功能说明:** - `enable_write: true` - 允许客户端写入,写入请求透传到真实从站 - `enable_write: false` - 只读模式,拒绝所有写入请求 ## 📊 使用场景 ### 场景1: 生产线监控 ``` 问题: 西门子PLC只支持2个客户端同时连接,但需要HMI、SCADA、MES同时读取 解决: PLC (192.168.1.10:502) ↓ Proxy (0.0.0.0:502) ↓ ├─ HMI客户端 ├─ SCADA系统 ├─ MES系统 └─ 数据采集服务 ``` **配置:** ```json { "master": { "slave_ip": "192.168.1.10", "slave_port": "502", "start_address": 0, "quantity": 200, "poll_interval": 100, "function_code": 3 }, "slave": { "listen_address": "0.0.0.0:502", "enable_write": true } } ``` ### 场景2: 传感器数据采集 ``` 问题: 温湿度采集器只支持单客户端,需要多个系统同时读取 解决: 传感器 (192.168.1.20:502) ↓ Proxy (0.0.0.0:1502) ↓ ├─ 环境监控系统 ├─ 报警系统 └─ 数据记录服务 ``` **配置:** ```json { "master": { "slave_ip": "192.168.1.20", "slave_port": "502", "start_address": 0, "quantity": 10, "poll_interval": 500, "function_code": 4 }, "slave": { "listen_address": "0.0.0.0:1502", "enable_write": false } } ``` ### 场景3: 只读缓存代理 ``` 问题: 真实设备响应慢,影响多个客户端轮询 解决: 代理定期轮询缓存,客户端秒级响应 ``` **配置:** ```json { "master": { "poll_interval": 200, "function_code": 4 }, "slave": { "enable_write": false } } ``` ## 🎮 运行方式 ### Linux 后台运行 #### systemd 服务(推荐) 创建服务文件 `/etc/systemd/system/modbus-proxy.service`: ```ini [Unit] Description=Modbus TCP Multiplex Proxy After=network.target [Service] Type=simple User=root WorkingDirectory=/opt/modbus-proxy ExecStart=/opt/modbus-proxy/modbus_tcp_multiplex_proxy_linux_amd64 Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` 启动服务: ```bash sudo systemctl daemon-reload sudo systemctl enable modbus-proxy sudo systemctl start modbus-proxy sudo systemctl status modbus-proxy # 查看日志 sudo journalctl -u modbus-proxy -f ``` #### nohup 后台运行 ```bash nohup ./modbus_tcp_multiplex_proxy_linux_amd64 > proxy.log 2>&1 & # 查看日志 tail -f proxy.log # 查看进程 ps aux | grep modbus_tcp_multiplex_proxy # 停止 pkill -f modbus_tcp_multiplex_proxy ``` ### Windows 后台运行 #### 方式1: NSSM (推荐) ```cmd # 下载 NSSM: https://nssm.cc/download nssm install ModbusProxy "C:\path\to\modbus_tcp_multiplex_proxy_windows_amd64.exe" nssm set ModbusProxy AppDirectory "C:\path\to\" nssm start ModbusProxy # 管理服务 nssm stop ModbusProxy nssm restart ModbusProxy nssm remove ModbusProxy confirm ``` #### 方式2: 创建 Windows 服务 使用 PowerShell 管理员权限: ```powershell # 创建服务 New-Service -Name "ModbusProxy" ` -BinaryPathName "C:\path\to\modbus_tcp_multiplex_proxy_windows_amd64.exe" ` -DisplayName "Modbus TCP Proxy" ` -StartupType Automatic # 启动服务 Start-Service ModbusProxy # 查看状态 Get-Service ModbusProxy # 停止服务 Stop-Service ModbusProxy # 删除服务 Remove-Service ModbusProxy ``` ### Docker 运行 创建 `Dockerfile`: ```dockerfile FROM golang:1.21-alpine AS builder WORKDIR /app COPY . . RUN go mod download RUN go build -o modbus-proxy main.go FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/modbus-proxy . COPY config.json . EXPOSE 502 CMD ["./modbus-proxy"] ``` 构建和运行: ```bash docker build -t modbus-proxy . docker run -d --name modbus-proxy \ -v $(pwd)/config.json:/root/config.json \ -p 502:502 \ --restart always \ modbus-proxy ``` ## 📈 性能指标 | 指标 | 数值 | |------|------| | 响应时间 | < 1ms (从缓存) | | 并发客户端 | 100+ | | 单客户端QPS | 1000+ | | 总QPS | 受限于网卡和系统 | | 内存占用 | < 20MB | | CPU占用 | < 5% (单核) | ## 🔍 日志示例 ### 正常启动日志 ``` 2024/01/08 10:00:00 Modbus TCP Gateway 启动... 2024/01/08 10:00:00 使用开源库: github.com/goburrow/modbus + github.com/tbrandon/mbserver 2024/01/08 10:00:00 配置: 主站->192.168.1.100:502 功能码:读保持寄存器(03) 2024/01/08 10:00:00 配置: 地址0 数量100 间隔200ms 2024/01/08 10:00:00 配置: 从站监听0.0.0.0:502 写入:true 2024/01/08 10:00:00 主站连接: 192.168.1.100:502 2024/01/08 10:00:00 主站连接成功 2024/01/08 10:00:01 初始数据获取成功 2024/01/08 10:00:01 从站监听: 0.0.0.0:502 2024/01/08 10:00:01 运行中,按Ctrl+C退出... ``` ### 写入操作日志 ``` 2024/01/08 10:05:30 写单个寄存器成功 地址:100 值:1234 2024/01/08 10:05:35 写多个寄存器成功 地址:200 数量:10 ``` ### 错误日志 ``` 2024/01/08 10:10:00 读取失败(1): read tcp 192.168.1.100:502: i/o timeout 2024/01/08 10:10:05 连续错误,重连... 2024/01/08 10:10:05 主站连接失败: dial tcp 192.168.1.100:502: connect: connection refused, 5秒后重试 ``` ## ❓ 常见问题 ### Q1: 端口被占用怎么办? **A:** 修改 `listen_address` 使用其他端口 ```json { "slave": { "listen_address": "0.0.0.0:1502" // 使用1502端口 } } ``` ### Q2: Linux 502端口需要root权限? **A:** 有三种解决方案: 1. 使用 sudo 运行 ```bash sudo ./modbus_tcp_multiplex_proxy_linux_amd64 ``` 2. 使用非特权端口(>1024) ```json { "slave": { "listen_address": "0.0.0.0:1502" }} ``` 3. 设置 CAP_NET_BIND_SERVICE 权限 ```bash sudo setcap 'cap_net_bind_service=+ep' ./modbus_tcp_multiplex_proxy_linux_amd64 ``` ### Q3: 为什么slave_port是字符串? **A:** goburrow/modbus 库要求端口为字符串格式,内部会拼接为 "IP:端口" ### Q4: 写入后多久能读到新值? **A:** - 写入客户端: 立即能读到(立即更新本地寄存器) - 其他客户端: 最多等待一个轮询周期(如200ms) ### Q5: 功能码03和04有什么区别? **A:** - 功能码03: 读保持寄存器(可读写) - 功能码04: 读输入寄存器(只读) - 在代理中,两者读取的是同一份缓存数据 ### Q6: 如何监控代理运行状态? **A:** 查看日志: ```bash # systemd journalctl -u modbus-proxy -f # nohup tail -f proxy.log # Docker docker logs -f modbus-proxy ``` ### Q7: 支持串口Modbus RTU吗? **A:** 当前版本仅支持 Modbus TCP,不支持串口RTU ### Q8: 可以同时代理多个从站吗? **A:** 当前版本不支持,每个实例只能代理一个从站。如需代理多个从站,请运行多个实例,使用不同的配置文件和监听端口 ### Q9: 依赖下载失败怎么办? **A:** 设置国内代理: ```bash go env -w GOPROXY=https://goproxy.cn,direct go mod download ``` ### Q10: 连接真实从站失败? **A:** 检查清单: - [ ] 真实从站IP和端口是否正确 - [ ] 网络是否连通 (`ping 192.168.1.100`) - [ ] 防火墙是否放行 - [ ] 真实从站是否在线 - [ ] slave_id 是否匹配 ## 🔒 安全建议 1. **网络隔离**: 将代理部署在工控网络内,避免直接暴露到公网 2. **访问控制**: 使用防火墙限制客户端IP访问 3. **权限最小化**: 非必要不使用root权限运行 4. **日志监控**: 定期检查日志,发现异常及时处理 5. **写入权限**: 生产环境谨慎启用 `enable_write` ## 📝 构建脚本说明 ### Windows 构建脚本 (`build.ps1`) ```powershell # 使用方法 .\build.ps1 # 自定义输出名称 .\build.ps1 -BaseOutputName "my_proxy" ``` **功能:** - ✅ 自动清理 build 目录 - ✅ 复制 config.json 到 build 目录 - ✅ 交叉编译三个平台: - Linux AMD64 - Linux ARM64 - Windows AMD64 - ✅ 文件名包含时间戳 - ✅ 彩色输出,方便查看构建进度 **输出示例:** ``` build/ ├── modbus_tcp_multiplex_proxy_linux_amd64_20240108_103045 ├── modbus_tcp_multiplex_proxy_linux_arm64_20240108_103045 ├── modbus_tcp_multiplex_proxy_windows_amd64_20240108_103045.exe └── config.json ``` ## 🛠️ 开发指南 ### 目录结构 ``` . ├── main.go # 主程序 ├── config.json # 配置文件 ├── go.mod # Go模块依赖 ├── go.sum # 依赖校验 ├── build.ps1 # Windows构建脚本 ├── README.md # 项目文档 └── build/ # 构建输出目录 ``` ### 本地开发 ```bash # 克隆项目 git clone cd modbus-tcp-multiplex-proxy # 安装依赖 go mod download # 运行 go run main.go # 测试 go test ./... # 格式化代码 go fmt ./... ``` ## 📚 参考资料 - [Modbus协议规范](https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf) - [goburrow/modbus 文档](https://github.com/goburrow/modbus) - [tbrandon/mbserver 文档](https://github.com/tbrandon/mbserver) ## 📄 许可证 MIT License --- ## 🎯 Project Overview A high-performance Modbus TCP gateway proxy that solves the problem of industrial devices not supporting multiple concurrent client connections. **Core Features:** - ✅ Single real slave → Multiple concurrent client support - ✅ Periodic polling with cache, response time < 1ms - ✅ Read/Write passthrough with real-time sync - ✅ Built on mature open-source libraries - ✅ Cross-platform support (Linux/Windows/ARM) ## 🚀 Quick Start 1. Download the pre-built binary for your platform from [Releases](../../releases) 2. Extract to your target directory 3. Edit `config.json` configuration file 4. Run the program ```bash # Linux chmod +x modbus_tcp_multiplex_proxy_linux_amd64_* ./modbus_tcp_multiplex_proxy_linux_amd64_* # Windows modbus_tcp_multiplex_proxy_windows_amd64_*.exe ``` ## ⚙️ Configuration ```json { "master": { "slave_ip": "192.168.1.100", "slave_port": "502", "slave_id": 1, "start_address": 0, "quantity": 100, "poll_interval": 200, "timeout": 5, "reconnect_time": 5, "function_code": 3 }, "slave": { "listen_address": "0.0.0.0:502", "slave_id": 1, "enable_write": true } } ``` ## 📊 Supported Function Codes | Code | Name | Support | Description | |------|------|---------|-------------| | 0x03 | Read Holding Registers | ✅ | Read/Write registers | | 0x04 | Read Input Registers | ✅ | Read-only registers | | 0x06 | Write Single Register | ✅ | Write single register (passthrough) | | 0x10 | Write Multiple Registers | ✅ | Write multiple registers (passthrough) | ## 📝 License MIT License