隐式消息传递的架构:在 Python 中实现原始 CIP I/O
Source: Dev.to
Class 1 I/O 的挑战
Ethernet/IP(EIP)基于通用工业协议(CIP),该协议定义了两种主要的消息类型:
- 显式消息(Explicit Messaging,TCP 44818) – 请求/响应,用于配置和诊断。
- 隐式消息(Implicit Messaging,UDP 2222) – 循环 I/O,用于高速、重复的数据交换(Class 1 连接)。
在使用原始套接字而非商业驱动程序来建立这些复杂连接时,面临的架构挑战在于如何管理资源竞争以及时间确定性。
🏗️ 四步连接序列
一个稳健的实现需要仔细管理 TCP 的建立过程以及随后 UDP I/O 的生命周期。我们的架构在循环过程中遵循以下四个步骤:
注册会话(TCP)
过程从向目标设备的封装层(TCP 44818)发送显式消息开始。这一步会建立一个 Session Handle,用于标识后续请求的连接。
Forward Open(TCP)
这是最关键的一步。客户端发送 Forward Open 命令,包含建立 Class 1 I/O 连接所需的全部参数,包括:
- 请求的包间隔(RPI)
- 连接路径(用于 I/O 数据的 Assembly 实例 ID)
- 连接超时参数
设备返回 O2T(Originator to Target)和 T2O(Target to Origin)连接 ID。
循环 I/O 交换(UDP)
连接通过 TCP 建立后,系统切换到高速 UDP 2222。客户端使用已建立的 O2T 连接 ID 定期发送数据,设备则返回 T2O 数据。这样可以确保循环数据更新的最小延迟。
Forward Close 与拆除(TCP)
为了防止目标设备因超时而报告故障,客户端必须显式发送 Forward Close 命令(TCP),在关闭套接字之前优雅地释放设备分配的资源。
💻 确定性测试的架构
为了可靠地测试这套复杂的序列,我们的学习套件采用了双应用结构:
- 原始套接字客户端 – 实现完整的 TCP 与 UDP 状态机,管理循环的 Open/Close 序列。
- 模拟 PLC 服务器 – 在本地主机上运行的独立 Python 应用,监听 TCP 44818 和 UDP 2222。模拟服务器对于确定性测试至关重要,因为它在握手期间提供正确且即时的响应,使开发者能够将逻辑错误与物理层噪声分离。
Python I/O 循环结构
原始套接字实现需要精确的报文组装。下面是用于管理循环 UDP 交换的概念结构:
# Conceptual Structure for I/O Loop
while running:
# 1. Open Session (TCP) & Forward Open (TCP) is performed here...
# 2. UDP Exchange Phase:
try:
# Construct and send O2T packet using raw bytes
udp_socket.sendto(o2t_packet, (target_ip, 2222))
# Receive T2O packet (Target to Origin)
received_data, addr = udp_socket.recvfrom(1024)
# Log and parse the raw data...
except socket.timeout:
log("Warning: UDP I/O Timeout occurred.")
# 3. Forward Close (TCP) & TCP Disconnect is performed here...
time.sleep(RPI_WAIT_TIME)
🔒 结论
掌握 CIP 的原始套接字实现对从事工业网络安全、定制 SCADA 集成或嵌入式系统的开发者至关重要,尤其是在外部库体积过大或不可用的情况下。
我们已在 GitHub 上记录了该项目的完整架构和技术细节。如果您希望获取此框架的完整、可直接部署的 Python 源代码,并深入研究原始报文结构,以用于您自己的定制工业解决方案: