隐式消息传递的架构:在 Python 中实现原始 CIP I/O

发布: (2025年12月3日 GMT+8 11:26)
5 min read
原文: Dev.to

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 源代码,并深入研究原始报文结构,以用于您自己的定制工业解决方案:

查看完整项目及详细架构

Back to Blog

相关文章

阅读更多 »