# 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