在 Niagara Station 现场,经常会遇到几类网络转发需求:
- 把站上某个端口的 TCP/UDP 流量,转发到另一台设备的对应端口;
- 两边协议不一样——比如客户端只会发 TCP,目标设备只回 UDP(或反过来),需要做协议桥接;
- 不想在服务器上额外维护 socat / iptables / systemd 那一套,希望直接在 Niagara 里配置和观察。
glineSocat 就是为这类场景做的一个 Niagara 组件(类型:glineSocat:SocatChannel,平台 Niagara 4.14)。本文按“组件使用说明”的口径,把参数、四种转发模式、动作和常见排查点整理出来,现场照着填即可。
一、glineSocat 到底是什么
glineSocat 是一个跑在 Niagara 4 Station 里的组件。它做的事情非常朴素:
- 在本机起一个监听端口(
listenHost:listenPort); - 把收到的流量转发到另一台机器的另一个端口(
remoteHost:remotePort); - 支持 TCP 和 UDP 两种协议,并且支持 TCP ↔ UDP 协议桥接。
可以把它理解成一个内嵌在 Niagara 里的 socat——区别是不用写命令行,所有参数都在 Property Sheet 里填,改完点一下按钮就热重载。相比在主机上再挂一个 socat / 端口转发进程,它还有几个好处:
随站启停
挂 station 生命周期:站起它就起、站停它就停,不用写计划任务或系统服务。
无需外部软件
除模块本身,主机上不用安装任何额外程序。
可被 Niagara 管理
状态、字节统计、日志都是站点里的槽位,可在 Workbench / 网页 / oBIX 查看与操作。
二、五步跑起来
| 步骤 | 在 Workbench / 网页里做什么 |
|---|---|
| ① 放组件 | Workbench 切到 station,把 SocatChannel 拖到某个 Folder(如 PortForward/) |
| ② 填表格 | 打开组件的 Property Sheet,按下面的样例填参数 |
| ③ 保存 | 点 Save;网页端要双击改值再点 Save |
| ④ 启动 | 默认 enabled=true 会自动起;改过配置后点动作 restartRelay 热重载 |
| ⑤ 看状态 | 看 status / connCount / rxBytes / txBytes / log 确认在跑 |
记住一个对应关系:mode 选方向,listen* 是“从哪收”,remote* 是“往哪发”。
三、属性表全字段
前 9 个字段需要自己填,后 5 个是只读的运行观察值。
| 字段 | 类型 / 默认 | 说明 |
|---|---|---|
enabled | boolean / true | 总开关。true=组件启动时自动开监听;false=不自动起 |
mode | string / tcp-tcp | 转发方向,四选一:tcp-tcp / udp-udp / tcp-udp / udp-tcp |
listenHost | string / 0.0.0.0 | 本机监听地址。一般填 0.0.0.0(所有网卡)或本机 IP |
listenPort | int / 8090 | 本机监听端口,注意别和站里已有服务冲突 |
remoteHost | string / (空) | 目标地址(IP 或域名),必填,如 192.168.2.180 |
remotePort | int / 80 | 目标端口,如 80 / 161 / 502 |
backlog | int / 50 | TCP 等待接入队列长度(仅 tcp-tcp / tcp-udp 用),不是最大连接数 |
idleTimeoutMs | int / 0 | 空闲超时毫秒。0=不限;>0 时读写卡住超过该时间就断开。UDP 等回包也用这个值(0 时默认 4000ms) |
hexLog | boolean / false | true=把经过的报文按十六进制写进 log(调试用)。会让日志量明显增大,不建议长期开 |
status | 只读 | 运行状态,如 OK udp-udp listening 8090 |
rxBytes / txBytes | 只读 | 累计接收 / 发送字节数 |
connCount | 只读 | 累计连接数(只统计 TCP 接入;udp 模式下为 0) |
log | 只读 | 操作日志,最近 4000 字符 |
图 1 · SocatChannel 属性表(示意:本机监听 8090 → 目标 161,udp-udp)
四、四种转发模式
4.1 tcp-tcp —— TCP 进,TCP 出
把站上某端口转发到另一台设备的 TCP 端口。典型场景:网页管理口、Modbus TCP、任意 TCP 服务。
| 字段 | 填这个值 |
|---|---|
| mode | tcp-tcp |
| listenHost / listenPort | 0.0.0.0 / 8090 |
| remoteHost / remotePort | 192.168.2.180 / 80 |
| backlog / idleTimeoutMs / hexLog | 50 / 0 / false |
# 客户端访问站上的 8090,就等于访问目标设备的 80 curl http://192.168.2.112:8090/home.htm # status: OK tcp-tcp listening 8090 (connCount 会 +1)
数据流:客户端 → TCP 监听 8090 → 站内建 TCP 连到 180:80 → 双向对拷。若目标不可达或端口不通,log 会记 tcp bridge err,但 status 仍显示 listening。
4.2 udp-udp —— UDP 进,UDP 出
适合 SNMP 这类“一问一答”的 UDP 协议。每收到一个请求包,新开一个 UDP socket 发出去,等一个回包(最多等 idleTimeoutMs,默认 4000ms)再送回客户端。
| 字段 | 填这个值 |
|---|---|
| mode | udp-udp |
| listenHost / listenPort | 0.0.0.0 / 8091 |
| remoteHost / remotePort | 192.168.2.180 / 161 |
| idleTimeoutMs | 0(或 4000 等回包) |
snmpget -v1 -c public 192.168.2.112:8091 1.3.6.1.4.1.318.1.1.12.1.5.0 # 期望: 读回设备型号字符串
4.3 tcp-udp —— TCP 进,UDP 出(桥接)
客户端只会说 TCP,目标设备只会说 UDP。比如监控平台用 TCP 连过来,组件把字节原样塞进 UDP 包发给目标的 SNMP 端口。
| 字段 | 填这个值 |
|---|---|
| mode | tcp-udp |
| listenHost / listenPort | 0.0.0.0 / 8092 |
| remoteHost / remotePort | 192.168.2.180 / 161 |
| hexLog | true(调试期可开) |
数据流:客户端开 TCP 连到 8092 → 站把 TCP 字节原样丢进一个 UDP 包发给 180:161 → 等 UDP 回包 → 原样写回 TCP 连接。
4.4 udp-tcp —— UDP 进,TCP 出(反向桥接)
反向桥接:客户端只发 UDP,目标是 HTTP/TCP 服务。
| 字段 | 填这个值 |
|---|---|
| mode | udp-tcp |
| listenHost / listenPort | 0.0.0.0 / 8093 |
| remoteHost / remotePort | 192.168.2.180 / 80 |
| idleTimeoutMs | 0(或自定义) |
数据流:收到一个 UDP 包 → 新建一条 TCP 连到 180:80 → 把包内容发过去 → 读一次 TCP 响应 → 用 UDP 发回给客户端。
4.5 四种模式速查
| mode 填法 | 客户端 → 站 | 站 → 目标 | 典型用途 |
|---|---|---|---|
| tcp-tcp | TCP(长连接) | TCP(长连接) | 网页转发、Modbus TCP、任意 TCP 服务 |
| udp-udp | UDP 一问一回 | UDP 一问一回 | SNMP 轮询、简单 UDP 查询 |
| tcp-udp | TCP | UDP | 客户端只会 TCP,设备只回 UDP(如 SNMP 桥接) |
| udp-tcp | UDP | TCP | 客户端只会 UDP,目标是 TCP/HTTP 服务 |
选法:两边协议相同 → tcp-tcp / udp-udp;两边协议不同 → tcp-udp / udp-tcp 桥接。
五、日志里那条 “no matching transaction” 怎么读
在 udp-udp 转 SNMP 的调试过程中,日志里出现过这样一条:
Receive response with no matching transaction from null
先把截图里的字段读清楚,再判断它属于哪一层的问题:
| 字段 | 截图值 | 含义 |
|---|---|---|
| ReqID | 0 | 本地没有找到对应的请求事务 ID |
| Var Bind | 1.3.6.1.6.3.15.1.1.5.0 COUNTER: 640 | OID 是 usmStatsUnknownEngineIDs,表示“未知的 SNMP Engine ID”计数 |
| pduType | 168 | 对应 SNMP Report PDU,不是普通响应 |
| isResponse | true | 这是一个响应包 |
| source IP / Remote Port | 192.168.2.180 / 161 | 来自 PDU 本身 |
从 OID 和 pduType 看,这条日志更像是 SNMPv3 USM 层的问题:目标设备在报 usmStatsUnknownEngineIDs,意思是它收到的请求里 Engine ID 对不上,于是回了一个 Report PDU 说明情况。这属于 SNMP 协议层 / 认证层的不匹配,而不是 glineSocat 中继本身超时或丢包的典型表现。
因此排查顺序建议这样:
- 先看协议层:确认客户端和目标设备用的 SNMP 版本(v1/v2c/v3)、community 或 USM 用户名 / Engine ID 是否对齐。OID 落在
usmStats*这一组时,先查 SNMPv3 的 Engine ID 协商和认证配置。 - 再看中继层:确认 remoteHost/remotePort 填对、目标 161 端口可达、idleTimeoutMs 给够(默认等回包约 4000ms)。
- 最后看轮询节奏:短时间高频并发请求、回包乱序到达,也可能让本地事务对不上,但这通常伴随连接数和重传异常,不会单独以
usmStatsUnknownEngineIDs的形式出现。
图 2 · hexLog=true 时的十六进制报文日志(调试用,看完请关掉)
六、并发与 CPU 消耗
| 问题 | 实际情况 |
|---|---|
| 有没有最大客户端数? | 组件里没有 maxClients 属性,代码未做并发上限 |
| 每个客户端一个线程? | TCP 每连接 1 个处理线程;UDP 桥接是每收到一个包起一个线程 |
| backlog 是上限吗? | 不是,它只是“已握手、未 accept”的排队缓冲,默认 50 |
| 真正的天花板在哪? | JVM 线程数 / 文件描述符 / 内存 |
CPU:空闲或少量连接时线程阻塞在 read(),几乎不占 CPU;数据吞吐是纯 IO 搬运,开销很低。主要成本在长连接数——每多一条 TCP 长连接就多一个线程。工程上普通场景 <200 并发 TCP 连接无压力;大批长连接建议把 idleTimeoutMs 设上限,hexLog 不要长期开。
七、改完配置怎么生效:5 个动作
动作在属性表的 Actions 区。最关键的是 restartRelay——改了 mode / 端口 / remoteHost 等属性后,监听不会自动重建,必须点一次。
| 动作 | 作用 | 什么时候点 |
|---|---|---|
| startRelay | 启动监听 / 中继 | enabled=false 时手动起 |
| stopRelay | 停止中继、断开所有连接 | 维护 / 换配置前 |
| restartRelay | 停止再启动(热重载) | 改完任何配置后点它 |
| clearLog | 清空 log | 日志太长时 |
| clearStats | rxBytes/txBytes/connCount 归零 | 重新测流量前 |
# oBIX 调动作(命令行) curl -u 'Obix:<密码>' -X POST \ 'http://HOST/obix/config/PortForward/SocatChannel/restartRelay/' # 网页端: 属性表下方 Actions 区,找到 restartRelay 点一下
八、常见问题排查
| 现象 | 可能原因 | 怎么处理 |
|---|---|---|
| status = ERR BindException | listenPort 被占用 | 换端口,或先停占用它的服务 |
| status 一直 listening 但连不上 | 防火墙 / 目标不可达 | 查站所在机器防火墙;看 log 有没有 bridge err |
| log 里 tcp bridge err | remoteHost/remotePort 填错或设备没开 | ping/curl 目标,确认端口开放 |
| 改了配置没变化 | 没热重载 | 点 restartRelay |
| UDP 收不到回包 | 目标不回 / 回太慢 | 调大 idleTimeoutMs;确认协议是一问一回 |
| 数据能通但看不懂 | 想抓包 | hexLog=true 看十六进制日志(用完关掉) |
九、用 oBIX 脚本化配置
不想点网页时,可以用 oBIX 写属性。注意必须 PUT 带 obix XML,POST 纯文本会报错。
# 读全部属性 curl -u 'Obix:<密码>' 'http://HOST/obix/config/PortForward/SocatChannel/' # 写字符串(PUT + obix XML) curl -u 'Obix:<密码>' -X PUT -H 'Content-Type: text/xml' \ --data '<str val="192.168.2.180"/>' '.../SocatChannel/remoteHost/' # 写整数 curl ... -X PUT --data '<int val="80"/>' '.../SocatChannel/remotePort/' # 写布尔 curl ... -X PUT --data '<bool val="true"/>' '.../SocatChannel/enabled/' # 错误做法(会失败):POST 纯文本 → EOFException;POST <str> → 类型转换错误 # 调动作用 POST .../restartRelay/ ;创建对象请用 Workbench / 网页拖组件
十、小结
- mode 选方向:tcp-tcp / udp-udp / tcp-udp / udp-tcp;
- listen* 填“从哪收”,remote* 填“往哪发”;
- Save → 改过就点 restartRelay → 看 status / log 确认。
同协议转发用 tcp-tcp / udp-udp;两边协议不一致才用 tcp-udp / udp-tcp 桥接。日志里出现协议层(如 SNMP usmStats*)的回包时,先按对应协议本身的原因排查,再看中继层的超时和可达性。