首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Modbus TCP/IP、UDP、SNMP 三协议并行:以太网温湿度变送器在机房监控中的端口冲突与抓包分析

Modbus TCP/IP、UDP、SNMP 三协议并行:以太网温湿度变送器在机房监控中的端口冲突与抓包分析

原创
作者头像
盛世宏博科技
发布于 2026-09-22 14:39:47
发布于 2026-09-22 14:39:47
1150
举报

Modbus TCP/IP、UDP、SNMP 三协议并行:以太网温湿度变送器在机房监控中的端口冲突与抓包分析

近期在调试一套工业环境监控系统时,遇到了关于 Modbus 连接保持的问题。现场部署了一批 恒湿净化设备(包括 @恒湿消毒净化一体机 等),主要用来维持特定环境的温湿度指标。在长时间运行后,发现设备偶尔会掉线。把 Modbus TCP、UDP 和 SNMP 做成"三协议并行"之后,问题从"单协议轮询瓶颈"变成了"三个协议栈如何在同一颗 MCU 里互不干扰、端口如何分配、报文如何抓、如何判"——这才是高并发机房监控里最硬的工程细节。


一、三协议并行的工程动机

先说清楚:这不是炫技,是物理层复用、传输层分工、应用层互补。

代码语言:javascript
复制
以太网温湿度变送器(W5500 / LAN8720 + STM32F407)
  ├── Modbus TCP Server(端口 502)
  │     用途:网关周期轮询,拉取全量寄存器
  │     特点:可靠、有序、有连接状态
  │     负载:温度、湿度、露点、状态、质量戳
  │
  ├── UDP Server(端口 9000,私有协议)
  │     用途:事件 Trap 推送、越限秒级上报
  │     特点:无连接、低开销、不等轮询
  │
  └── SNMP Agent(端口 161 UDP)
        用途:标准网管查询、Trap 推送(端口 162)
        特点:标准化 MIB、与 NMS 集成、读/写/通知

三协议各司其职:

协议

角色

数据特征

可靠性要求

Modbus TCP

全量基线

周期性、结构化、多寄存器

高(必须读到)

UDP 私有

事件快报

瞬时、小包、越限/故障

中(可丢,但尽量快)

SNMP

标准管理

标量、表、Trap

中(Get 需响应,Trap 可丢)


二、端口分配与共存架构

2.1 端口规划

代码语言:javascript
复制
变送器 IP: 10.10.10.15
  TCP 502   → Modbus TCP(RW,全量寄存器)
  UDP 161   → SNMP Agent(RO,标准 MIB)
  UDP 162   → SNMP Trap 发送(主动推送)
  UDP 9000  → 私有事件 Trap(越限/故障)

关键设计决策:

  1. Modbus TCP 独占 TCP 502:标准端口,所有网关/SCADA 默认连 502。
  2. SNMP 用标准 UDP 161:与网络管理系统(NMS)对接,走标准 MIB-II 和私有 MIB。
  3. 私有 UDP 用非标准端口 9000:避免与 SNMP 冲突,且可独立限速/过滤。
  4. SNMP Trap 发往 162:NMS 监听 162 接收 Trap。

2.2 MCU 侧资源分配

代码语言:javascript
复制
LWIP 协议栈资源(STM32F407 + 192KB RAM):
  TCP PCB: 2 个(Modbus TCP,限制连接数)
  UDP PCB: 4 个(SNMP 161、SNMP Trap 162、私有 UDP 9000、DNS)
  pbuf 池: 32 个 × 512 bytes(Modbus TCP 大帧 + SNMP 小帧 + UDP Trap)

为什么 UDP PCB 给 4 个?

  • SNMP Get/Set 用 161,需要独立 PCB 监听
  • SNMP Trap 发送用 162,需要独立 PCB(或复用 161 但目标端口不同)
  • 私有 UDP 9000 独立 PCB,避免与 SNMP 互相阻塞
  • DNS 客户端占 1 个(NTP 校时、固件升级检查)

三、报文结构对比:三种协议同台

用 Wireshark 抓同一台变送器(IP: 10.10.10.15)的三种报文,逐层对比。

3.1 MAC 层(完全相同)

代码语言:javascript
复制
Ethernet II
  Src: 00:08:dc:ab:cd:ef (W5500 MAC)
  Dst: 00:0c:29:11:22:33 (Gateway MAC)
  Type: 0x0800 (IPv4)

3.2 IP 层(Protocol 字段区分)

代码语言:javascript
复制
Internet Protocol Version 4
  Protocol: 6 (TCP) / 17 (UDP)    ← TCP 用于 Modbus,UDP 用于 SNMP 和私有协议
  Source: 10.10.10.15
  Destination: 10.10.10.100 (网关/NMS)

3.3 传输层对比

字段

Modbus TCP (502)

SNMP (161)

私有 UDP (9000)

协议

TCP

UDP

UDP

头部

32 bytes (含 Options)

8 bytes

8 bytes

连接

有 (SYN/ACK/FIN)

无

无

可靠性

确认+重传

无

无

流量控制

滑动窗口

无

无

3.4 应用层数据

Modbus TCP(读 5 个输入寄存器):

代码语言:javascript
复制
MBAP Header:
  Transaction ID: 0x0001
  Protocol ID: 0x0000
  Length: 0x0006
  Unit ID: 0x01

PDU:
  Function Code: 0x04 (Read Input Registers)
  Starting Address: 0x0000
  Quantity: 0x0005

响应:
  Transaction ID: 0x0001
  Length: 0x000d
  Unit ID: 0x01
  Byte Count: 0x0a
  Registers: [0x00e2, 0x0216, 0x00c3, 0x0001, 0x0000]

SNMP GetRequest(读 sysDescr):

代码语言:javascript
复制
SNMPv2c
  Version: 2c
  Community: public
  PDU Type: GetRequest
  Request ID: 0x7a3f
  Error Status: 0 (noError)
  Error Index: 0
  VarBind List:
    sysDescr.0 (1.3.6.1.2.1.1.1.0)

响应:
  PDU Type: GetResponse
  sysDescr.0 = "EnvSensor-V2.1.3, STM32F407, LWIP-2.1.2"

私有 UDP Trap(越限事件):

代码语言:javascript
复制
Header (8 bytes):
  Magic: 0xAA55
  Version: 0x01
  Sensor ID: 0x00010023
  Payload Len: 0x0010

Payload:
  Timestamp: 0x5f3a_2c1b
  Temperature: 0x00e2 (22.6℃)
  Humidity: 0x0216 (53.4%RH)
  Dew Point: 0x00c3 (19.5℃)
  Alert Flags: 0x0001 (bit0=高温)
  CRC16: 0x8f4e

四、Wireshark 抓包实战:三协议同屏

4.1 抓包命令

代码语言:javascript
复制
# 在边缘网关上抓指定传感器的全协议流量
sudo tcpdump -i eth0 host 10.10.10.15 -w capture_3proto.pcap

# 或分协议抓
sudo tcpdump -i eth0 host 10.10.10.15 and port 502 -w modbus.pcap
sudo tcpdump -i eth0 host 10.10.10.15 and port 161 -w snmp.pcap
sudo tcpdump -i eth0 host 10.10.10.15 and port 9000 -w udp_trap.pcap

4.2 Wireshark 显示过滤器

代码语言:javascript
复制
# Modbus TCP 读请求
modbus.func_code == 4

# SNMP GetRequest
snmp.pdu_type == 0  # GetRequest

# SNMP Trap
snmp.pdu_type == 4  # Trap

# UDP 私有 Trap
udp.port == 9000 && data[0:2] == aa:55

# TCP 重传
tcp.analysis.retransmission

# TCP 零窗口
tcp.analysis.zero_window

# UDP 丢包(无直接标志,看 IO Graph 速率)

4.3 典型抓包场景

场景 1:正常三协议并行(时间轴)

代码语言:javascript
复制
No.  Time        Source          Destination     Protocol Info
1    0.000000    10.10.10.100   10.10.10.15     TCP      49152 → 502 [SYN]
2    0.000218    10.10.10.15    10.10.10.100    TCP      502 → 49152 [SYN, ACK]
3    0.000312    10.10.10.100   10.10.10.15     TCP      49152 → 502 [ACK]
4    0.000401    10.10.10.100   10.10.10.15     Modbus   Read Input Registers
5    0.000892    10.10.10.15    10.10.10.100    Modbus   Response
6    0.001234    10.10.10.100   10.10.10.15     TCP      49152 → 502 [FIN, ACK]
7    0.001456    10.10.10.15    10.10.10.100    TCP      502 → 49152 [FIN, ACK]
8    0.001523    10.10.10.100   10.10.10.15     TCP      49152 → 502 [ACK]

9    5.230000    10.10.10.100   10.10.10.15     SNMP     GetRequest sysDescr.0
10   5.230456    10.10.10.15    10.10.10.100    SNMP     GetResponse
11   5.230789    10.10.10.15    10.10.10.100    UDP      9000 → 9000 Trap (越限)

12   30.000000   10.10.10.15    10.10.10.100    SNMP     Trap (湿度越限)

场景 2:端口冲突导致 Modbus 连接被拒

代码语言:javascript
复制
No.  Time        Source          Destination     Protocol Info
15   30.500000   10.10.10.100   10.10.10.15     TCP      49153 → 502 [SYN]
16   30.500218   10.10.10.15    10.10.10.100    TCP      502 → 49153 [RST, ACK]

→ MCU TCP 连接池满(只有 2 个 PCB),被 SNMP/NMS 的 TCP 连接(如果有)或 Web 管理占用。

场景 3:SNMP 和私有 UDP 抢 pbuf

代码语言:javascript
复制
No.  Time        Source          Destination     Protocol Info
20   10.000000   10.10.10.100   10.10.10.15     SNMP     GetBulkRequest
21   10.000100   10.10.10.15    10.10.10.100    TCP      [ZeroWindow]
22   10.000200   10.10.10.15    10.10.10.100    UDP      9000 → 9000 Trap (丢失)

→ LWIP pbuf 池耗尽,Modbus TCP 响应发不出(ZeroWindow),私有 UDP Trap 被丢弃。


五、端口冲突的 5 种真实场景

场景 1:多主站抢 Modbus TCP 连接

代码语言:javascript
复制
症状:Modbus 间歇性 RST,SNMP 正常
原因:网关 A(新系统)+ 网关 B(旧 SCADA)+ Web 管理同时连 502
解决:限制 TCP PCB 数量,或旧系统改 RTU 轮询

场景 2:SNMP 和私有 UDP 共用 pbuf 池

代码语言:javascript
复制
症状:SNMP Get 超时,私有 UDP Trap 丢包
原因:LWIP pbuf 池满,两个 UDP 协议栈互相阻塞
解决:调大 pbuf 池,或 SNMP 和私有 UDP 用不同端口但共享 PCB(需固件支持)

场景 3:SNMP Trap 和私有 UDP Trap 同时触发

代码语言:javascript
复制
症状:两个 Trap 同时发,NMS 只收到一个
原因:MCU 发送队列满,后到的 Trap 被丢弃
解决:Trap 合并策略,或优先级排序(SNMP Trap 优先)

场景 4:交换机端口安全限制

代码语言:javascript
复制
症状:传感器 MAC 被交换机封锁
原因:端口安全策略限制 MAC 数量(1 个),但传感器可能有多个 IP?
解决:检查传感器是否有多个 IP 或 IPv6 链路本地地址

场景 5:防火墙/ACL 拦截

代码语言:javascript
复制
症状:Modbus 通,SNMP 不通
原因:交换机 ACL 只允许 502 端口,拦截 161/162
解决:调整 ACL 规则,放行 SNMP 端口

六、MCU 侧三协议共存设计

6.1 LWIP 配置

代码语言:javascript
复制
// lwipopts.h
#define LWIP_TCP               1
#define LWIP_UDP               1
#define MEMP_NUM_TCP_PCB       2       // Modbus TCP 连接数
#define MEMP_NUM_UDP_PCB       4       // SNMP + Trap + 私有 UDP + DNS
#define PBUF_POOL_SIZE         32      // 内存池
#define PBUF_POOL_BUFSIZE      512     // 每 pbuf 大小
#define TCP_WND                2048    // TCP 窗口
#define TCP_SND_BUF            2048
#define TCP_MSS                256

6.2 任务分离

代码语言:javascript
复制
// 三个独立任务,不同优先级
void modbus_tcp_task(void *arg) {
    // 处理 Modbus TCP 请求,优先级中
}

void snmp_task(void *arg) {
    // 处理 SNMP Get/Set,优先级低
}

void udp_trap_task(void *arg) {
    // 处理私有 UDP Trap,优先级高(事件驱动)
}

6.3 发送队列优先级

代码语言:javascript
复制
发送优先级(从高到低):
  1. UDP Trap(越限事件,最高优先)
  2. Modbus TCP 响应(已建立的连接)
  3. SNMP Trap(标准 Trap)
  4. SNMP GetResponse(可延迟)
  5. 私有 UDP 心跳(最低优先)

七、Python 侧三协议并发采集

代码语言:javascript
复制
import asyncio
from pymodbus.client import AsyncModbusTcpClient
from pysnmp.hlapi import *
import socket
import struct

# Modbus TCP 轮询
async def poll_modbus(ip, interval=30):
    client = AsyncModbusTcpClient(ip, port=502)
    await client.connect()
    while True:
        result = await client.read_input_registers(0, 5)
        if not result.isError():
            temp = result.registers[0] * 0.1
            hum = result.registers[1] * 0.1
            dew = result.registers[2] * 0.1
            print(f"Modbus: T={temp}, H={hum}, D={dew}")
        await asyncio.sleep(interval)

# SNMP 查询
async def poll_snmp(ip, interval=60):
    while True:
        for (errorIndication, errorStatus, errorIndex, varBinds) in getCmd(
            SnmpEngine(),
            CommunityData('public'),
            UdpTransportTarget((ip, 161)),
            ContextData(),
            ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'))
        ):
            if errorIndication:
                print(f"SNMP error: {errorIndication}")
            else:
                for varBind in varBinds:
                    print(f"SNMP: {varBind}")
        await asyncio.sleep(interval)

# UDP Trap 监听
async def udp_trap_listener(port=9000):
    loop = asyncio.get_running_loop()
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(('0.0.0.0', port))
    sock.setblocking(False)
    while True:
        data, addr = await loop.sock_recvfrom(sock, 1024)
        frame = parse_udp_frame(data)
        if frame:
            print(f"UDP Trap from {addr}: {frame}")

async def main():
    tasks = [
        asyncio.create_task(poll_modbus('10.10.10.15')),
        asyncio.create_task(poll_snmp('10.10.10.15')),
        asyncio.create_task(udp_trap_listener(9000)),
    ]
    await asyncio.gather(*tasks)

八、InfluxDB 统一存储

代码语言:javascript
复制
# 三协议数据统一写入 InfluxDB
def write_to_influx(protocol, sensor_id, measurement, fields, tags=None, ts=None):
    point = build_line_protocol(
        sensor_id=sensor_id,
        measurement=measurement,
        tags={**(tags or {}), 'protocol': protocol},
        fields=fields,
        ts=ts or int(time.time() * 1e9)
    )
    await write_influx(point)

# Modbus TCP 数据
write_to_influx('modbus_tcp', 'th-001', 'environment', {
    'temperature': 22.6,
    'humidity': 53.4,
    'dew_point': 19.5,
})

# SNMP 数据
write_to_influx('snmp', 'th-001', 'environment', {
    'sys_uptime': 123456,
    'poe_voltage': 48.2,
})

# UDP Trap 数据
write_to_influx('udp_trap', 'th-001', 'alert_event', {
    'temperature': 22.8,
    'humidity': 65.2,
    'alert_flags': 1,
})

九、三协议并行的 5 个关键经验

  1. 端口规划先行:Modbus TCP 用 502,SNMP 用 161/162,私有 UDP 用非标准端口,避免冲突。
  2. 资源隔离:LWIP 的 pbuf 池、TCP PCB、UDP PCB 要按协议分配,避免互相抢占。
  3. 优先级明确:事件 Trap > Modbus 响应 > SNMP 响应 > 心跳。
  4. Wireshark 是眼睛:上线前抓包验证三协议并行,确认无 RST、无 ZeroWindow、无丢包。
  5. 统一存储:三协议数据统一写入 InfluxDB,按 protocol 标签区分,便于查询和分析。

十、一句话总结

Modbus TCP、UDP、SNMP 三协议并行,不是简单的"三个端口同时监听",而是物理层共享、传输层分工、应用层互补的工程平衡。​ TCP 管全量可靠,UDP 管事件秒级,SNMP 管标准管理,Wireshark 验证,InfluxDB 统一存储,边缘网关做时间对齐和去重——这才是高并发机房监控里经得起生产考验的三协议架构。

物联网 #Modbus #TCP/IP #UDP #POE供电 #腾讯云 #Wireshark #Python #InfluxDB #以太网温湿度传感器 #网口温湿度变送器 #机房监控


下一篇候选:

  1. 《LWIP 协议栈调优实战:pbuf 池大小、TCP 窗口与 UDP 缓冲区在三协议并行中的参数配置》
  2. 《SNMP MIB 设计:温湿度变送器的私有 MIB 定义与 NMS 集成实践》
  3. 《三协议并存时的网络安全:从交换机 ACL 到 IoT Explorer 设备认证的防护体系》

敬请关注!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Modbus TCP/IP、UDP、SNMP 三协议并行:以太网温湿度变送器在机房监控中的端口冲突与抓包分析
    • 一、三协议并行的工程动机
    • 二、端口分配与共存架构
      • 2.1 端口规划
      • 2.2 MCU 侧资源分配
    • 三、报文结构对比:三种协议同台
      • 3.1 MAC 层(完全相同)
      • 3.2 IP 层(Protocol 字段区分)
      • 3.3 传输层对比
      • 3.4 应用层数据
    • 四、Wireshark 抓包实战:三协议同屏
      • 4.1 抓包命令
      • 4.2 Wireshark 显示过滤器
      • 4.3 典型抓包场景
    • 五、端口冲突的 5 种真实场景
      • 场景 1:多主站抢 Modbus TCP 连接
      • 场景 2:SNMP 和私有 UDP 共用 pbuf 池
      • 场景 3:SNMP Trap 和私有 UDP Trap 同时触发
      • 场景 4:交换机端口安全限制
      • 场景 5:防火墙/ACL 拦截
    • 六、MCU 侧三协议共存设计
      • 6.1 LWIP 配置
      • 6.2 任务分离
      • 6.3 发送队列优先级
    • 七、Python 侧三协议并发采集
    • 八、InfluxDB 统一存储
    • 九、三协议并行的 5 个关键经验
    • 十、一句话总结
      • 物联网 #Modbus #TCP/IP #UDP #POE供电 #腾讯云 #Wireshark #Python #InfluxDB #以太网温湿度传感器 #网口温湿度变送器 #机房监控
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档