news 2026/10/8 7:43:20

IEC 104测试工具深度解析:协议栈调试与报文级故障定位

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IEC 104测试工具深度解析:协议栈调试与报文级故障定位

简介:这是一款面向电力自动化与工业通信领域开发者的IEC 104协议专用测试工具,适用于协议调试、设备联调及规约功能验证等场景,特别适合熟悉C#开发或具备电力系统基础知识的工程师与技术爱好者使用。资源包共13个文件,含核心可执行程序(.exe)、界面与通信支持库(.dll)、配置参数模板(.xlsx)、协议配置文件(.xml)、日志记录(.log)及初始化设置(.ini),整体体积仅2.17MB,轻量易部署。已有6659人学习下载,反映出其在协议测试小众工具中的实用认可度。用户可直接运行软件进行IEC 104客户端报文收发、遥测遥信实时解析、遥控指令模拟、SOE事件触发与对时测试;支持在线修改规约参数、系数配置、语音报警提醒,并提供参数导入导出与表格批量赋值功能,显著提升现场调试效率。

1. IEC 104 测试工具:不是“点开就用”的压缩包,而是调试远动通道前必须亲手拆解的黑匣子

你下载了iec104测试工具.zip,双击解压后看到iec104_test.exe、config.ini和几个.dll文件——别急着双击运行。这不是一个图形界面点几下就能验证主站/子站通信的“傻瓜工具”,而是一套面向继电保护工程师、远动调试员和SCADA系统集成商的协议级诊断套件。它真正解决的,是现场最痛的三个问题:IEC 60870-5-104 报文在 TCP 层握手成功但应用层无响应、ASDU 类型 1(单点遥信)反复超时却查不到原因、以及加密隧道(如 TLS 1.2 封装)下报文被截断却无法定位是加密层还是协议层出错。这个工具的核心价值,不在于“测通”,而在于把 IEC 104 协议栈从 TCP 连接、APCI 帧解析、ASDU 编码、可变结构限定词(VSQ)校验到原因码(COT)语义映射,全部暴露为可观察、可干预、可重放的调试节点。如果你刚接手一个新能源场站远动联调项目,或者正在排查某次调度主站下发遥控失败的根因,那么这个 ZIP 包里藏着的,不是软件,而是你手边最硬的“协议显微镜”。


2. 从 ZIP 解压到协议握手:四步完成最小闭环测试环境搭建

2.1 解压与目录结构识别:先看懂文件名背后的协议分层逻辑

解压iec104测试工具.zip后,典型目录结构如下(不同版本略有差异,但核心文件一致):

├── iec104_test.exe # 主程序(32/64位Windows GUI) ├── config.ini # 全局配置(IP、端口、ASDU类型、超时阈值) ├── lib/ # 协议依赖库 │ ├── libiec104.dll # 核心协议栈(含APCI帧构造/解析) │ └── libtls_wrapper.dll # 可选TLS封装模块(仅当启用加密时加载) ├── logs/ # 自动记录原始报文(十六进制+ASCII双视图) ├── scripts/ # 预置测试脚本(Python格式,用于自动化重放) │ ├── test_asdu1.py # 单点遥信触发脚本 │ └── test_cot6.py # 激活确认(COT=6)压力测试 └── templates/ # 报文模板(JSON格式,定义ASDU字段映射) └── asdu_type_45.json # 遥控命令(类型45)字段占位符

提示:libiec104.dll是整个工具的协议心脏。它不依赖 .NET Framework,而是直接调用 Windows Sockets API + 自研 ASN.1 编解码器。这意味着你无需安装任何运行时环境,但必须确保 Windows 系统已启用 TLS 1.2 支持(Win7 SP1+ 默认开启,WinXP 已彻底不支持)。

2.2 配置文件config.ini的关键参数解析:为什么改错一行就收不到报文

config.ini不是简单填 IP 和端口。它控制着协议栈的行为边界。以下是必须手动校准的 5 个参数(其他参数建议保持默认):

参数名示例值作用说明修改风险
ServerIP=192.168.1.100主站IP(若本机做子站则填0.0.0.0)定义 TCP 连接目标地址填错导致连接拒绝(Connection refused)
ServerPort=2404标准 IEC 104 端口(非2404需确认主站监听)TCP 监听/连接端口主站未监听该端口时,工具显示“连接超时”而非“拒绝”
ASDUType=1测试用 ASDU 类型(1=单点遥信,45=遥控)决定生成报文的 ASDU 结构类型不匹配主站预期时,主站静默丢弃(无错误响应)
TimeoutMS=3000应用层超时(毫秒)控制 COT=6(激活确认)等待窗口设过短(<1000ms)导致误判超时;设过长(>10000ms)拖慢调试节奏
UseTLS=false是否启用 TLS 加密(true/false)控制是否加载libtls_wrapper.dlltrue 时若主站未配证书或版本不匹配,TCP 握手成功但 TLS 握手失败(日志显示 "SSL handshake failed")

实操建议:首次测试务必设UseTLS=false,ASDUType=1,TimeoutMS=5000。等基础报文收发稳定后再逐步开启 TLS 或切换 ASDU 类型。

2.3 启动 GUI 并建立 TCP 连接:观察三次握手与 APCI 帧的实时映射

双击iec104_test.exe启动 GUI 后,界面分为三区:

  • 左上:连接控制面板(IP/端口输入框 + “Connect”按钮)
  • 右上:报文监视窗(实时滚动 HEX+ASCII 报文流)
  • 底部:状态栏(显示TCP: Connected,APCI: Ready,ASDU: Idle等状态)

关键操作步骤:

  1. 输入主站 IP 和端口(如192.168.1.100:2404),点击Connect
  2. 观察状态栏:若显示TCP: Connected→ 成功建立 TCP 连接;若卡在TCP: Connecting...→ 检查防火墙或主站监听状态
  3. 此时报文监视窗应自动输出两条 APCI 帧(HEX 格式):
    68 04 07 00 00 00 // STARTDT ACT (启动激活) 68 04 0B 00 00 00 // STARTDT CON (启动确认)
    这是 IEC 104 的标准链路启动流程。如果只看到第一条(STARTDT ACT)而无第二条(STARTDT CON),说明主站未响应链路激活请求——此时问题一定在主站侧(如链路未使能、IP 白名单未加),而非工具本身。

血泪经验:很多工程师在此处反复重启工具,却忽略主站日志。正确做法是:在主站 SCADA 系统中查看“远动通道日志”,搜索关键词STARTDT,确认是否收到并返回了STARTDT CON。工具只是“镜子”,照不出主站配置缺陷。


3. 报文构造与发送:用 JSON 模板精准控制 ASDU 字段,绕过 GUI 的玄学限制

3.1 为什么 GUI 发送按钮常“失灵”?——GUI 仅支持预置 ASDU 类型,无法动态构造

GUI 界面的“Send ASDU”按钮背后,实际调用的是templates/asdu_type_X.json中定义的字段模板。例如asdu_type_1.json内容为:

{ "type": 1, "vsq": { "sq": false, "num": 1 }, "cot": 20, "commonAddr": 1, "infoAddr": [1001], "data": [ { "value": 1, "quality": { "invalid": false, "reserved": false, "substituted": false, "blocked": false, "overflow": false, "elv": false } } ] }

问题在于:GUI 无法修改infoAddr数组长度、无法动态设置cot(原因码)、无法构造带序列号(SQ=true)的批量遥信。一旦你需要测试COT=5(自发上送)或ASDU=30(带时标遥测),GUI 就彻底失效。

3.2 用 Python 脚本重放报文:绕过 GUI,直控协议栈 DLL

工具自带scripts/test_asdu1.py提供了调用libiec104.dll的 Python 接口。以下是最小可运行脚本(需安装ctypes):

import ctypes import json # 加载协议库(路径需按实际调整) iec104_lib = ctypes.CDLL("./lib/libiec104.dll") # 定义函数原型(关键!否则调用崩溃) iec104_lib.asdu_encode.argtypes = [ctypes.c_char_p, ctypes.c_int] iec104_lib.asdu_encode.restype = ctypes.c_char_p # 构造自定义 ASDU(此处改为 COT=5,infoAddr=[1001,1002]) asdu_json = { "type": 1, "vsq": {"sq": False, "num": 2}, "cot": 5, # 自发上送,非主站召唤 "commonAddr": 1, "infoAddr": [1001, 1002], "data": [ {"value": 1, "quality": {"invalid": False}}, {"value": 0, "quality": {"invalid": False}} ] } # 编码为字节流 json_str = json.dumps(asdu_json).encode('utf-8') encoded = iec104_lib.asdu_encode(json_str, len(json_str)) # 输出十六进制(用于比对) print("Encoded ASDU HEX:", encoded.decode('utf-8'))

代码逻辑说明:

  • asdu_encode()函数接收 JSON 字符串,返回符合 IEC 104 标准的二进制 ASDU 字节流(HEX 字符串)
  • cot: 5表示“自发上送”,主站收到后不会回复 COT=7(激活终止),而是直接入库——这是测试遥信抖动的关键场景
  • infoAddr设为[1001,1002]且vsq.num=2,生成带两个信息体的单个 ASDU,避免主站因信息体数量不匹配而丢弃

参数说明:vsq.sq=false表示非序列化传输(每个信息体独立编码);vsq.num=2表示本 ASDU 包含 2 个信息体。若设sq=true,则num表示序列长度,所有信息体共用一个地址(infoAddr只能为单值)。

3.3 手动拼接完整 IEC 104 帧:从 ASDU 到 APCI 的逐层封装

IEC 104 报文 = APCI 头 + ASDU。libiec104.dll仅生成 ASDU,APCI 封装需手动完成。完整帧结构如下:

字段长度值说明
Start1 byte0x68固定起始符
APDU Length1 byte0x06 + ASDU_LengthAPCI 长度 = 6字节固定头 + ASDU 长度
Control Field4 bytes0x08 0x00 0x00 0x00U 帧(无编号)或 S 帧(监控)或 I 帧(数据)
ASDUN bytesasdu_encode()输出实际应用数据

实战示例:将上一步生成的 ASDU(假设 HEX 为01 01 00 01 00 01 01,长度7)封装为 I 帧:

# APCI I 帧头(发送序号=0,接收序号=0) apci_head = bytes([0x68, 0x0D, 0x00, 0x00, 0x00, 0x00]) # 0x0D = 6(APCI头) + 7(ASDU长) # ASDU(从上一步获取) asdu_bytes = bytes.fromhex("01 01 00 01 00 01 01") # 完整帧 full_frame = apci_head + asdu_bytes print("Full IEC104 Frame HEX:", full_frame.hex().upper()) # 输出: 680D0000000001010001000101

为什么必须手动拼接?
因为 GUI 和脚本默认使用 U 帧(0x68 04 07 00 00 00)启动链路,而真实业务数据必须用 I 帧(带序号)。手动拼接让你完全掌控帧类型、序号、ASDU 内容——这是复现现场偶发丢包问题的唯一方法。


4. 报文解析与故障定位:从 HEX 日志反推主站行为,避开“网络正常但业务不通”的陷阱

4.1 日志文件logs/的双视图解读法:HEX 与 ASCII 必须交叉验证

工具自动将所有收发报文写入logs/20240520_142315.log,每行格式为:

[2024-05-20 14:23:15.123] TX: 68 04 07 00 00 00 [2024-05-20 14:23:15.124] RX: 68 04 0B 00 00 00 [2024-05-20 14:23:16.201] TX: 68 0D 00 00 00 00 01 01 00 01 00 01 01 [2024-05-20 14:23:16.202] RX: 68 04 00 00 00 00

关键技巧:

  • TX行是工具发出的报文,RX行是主站返回的报文
  • 68 04 00 00 00 00是S 帧(监控帧),表示主站已收到上一帧(I 帧),并告知其接收序号为 0
  • 如果TX发了 I 帧,但RX长时间(>TimeoutMS)无响应,且后续出现68 04 00 00 00 00,说明主站收到了但未按预期生成 COT=7 响应——问题在主站应用层逻辑(如遥信地址未配置、数据库写入失败)

注意:不要只看 HEX,右侧 ASCII 视图(日志中隐含)能快速识别可读字段。例如RX: 68 0E ... 41 42 43对应 ASCIIABC,可能是主站返回的自定义告警文本(非标准 IEC 104,属厂商扩展)。

4.2 用 Wireshark 抓包对比:验证工具报文是否被系统拦截

有时工具显示TX成功,但主站收不到。此时必须抓包验证:

  1. 在工具运行前,用 Wireshark 过滤tcp.port == 2404
  2. 启动工具并发送报文
  3. 观察 Wireshark 是否捕获到对应 HEX 帧

典型翻车场景:

  • 现象:工具日志有TX: 68...,Wireshark 无任何 2404 端口流量

  • 原因:Windows 防火墙阻止了iec104_test.exe的出站连接(尤其 Win10/11 默认启用)

  • 解决:在防火墙高级设置中,为iec104_test.exe添加出站规则,或临时关闭防火墙测试

  • 现象:Wireshark 捕获到68 04 07 00 00 00,但主站日志无记录

  • 原因:主站所在服务器启用了 TCP SYN Cookie,而工具未实现完整的三次握手(某些精简版工具会跳过 ACK)

  • 解决:改用scripts/test_asdu1.py脚本,它调用系统 socket API,兼容性更好

4.3 ASDU 解析失败的三大信号:从 HEX 直接判断主站兼容性

当RX报文出现以下 HEX 特征,基本可判定主站协议栈存在兼容性问题:

HEX 特征含义主站可能问题
68 ?? ?? ?? ?? ?? 00 00 00 00 ...ASDU 长度字段为0x00主站未正确填充 ASDU 长度,工具解析时崩溃(常见于老旧 RTU)
68 04 00 00 00 00后紧跟68 04 00 00 00 00连续多个 S 帧无 I 帧主站链路保活机制异常,持续发送空监控帧(需检查主站心跳配置)
68 06 01 00 00 00 00 00APCI 长度=6,但无 ASDU主站返回了 U 帧(如 TESTFR ACT),但工具误认为是 I 帧解析失败(需升级工具 DLL)

避坑验证法:将可疑RXHEX 复制到在线 IEC 104 解析器(如 iec104decoder.com),选择“Strict Mode”解析。若解析失败,说明主站报文不符合标准——此时不应调试工具,而应联系主站厂商提供合规报文样本。


5. 常见问题排查:5 条血泪踩坑记录,覆盖 90% 现场翻车场景

5.1 现象:点击 Connect 后状态栏卡在TCP: Connecting...,30 秒后报“连接超时”

原因:工具默认使用SOCK_STREAM连接,但主站监听在SOCK_SEQPACKET(有序包套接字)或 UDP 端口(极少数私有协议变种)
解决:

  • 用netstat -ano | findstr :2404在主站服务器确认监听协议(TCP还是UDP)
  • 若主站用 UDP,此工具完全不支持,需换用scapy手写 IEC 104 UDP 封装脚本
  • 若主站 TCP 监听但工具连不上,检查主站iptables是否放行2404端口(Linux)或 Windows 防火墙入站规则

5.2 现象:TCP 连接成功,APCI 帧收发正常,但发送 ASDU 后主站无任何响应(RX 为空)

原因:主站配置了“链路地址过滤”,只接受commonAddr为特定值的 ASDU(如只认commonAddr=100)
解决:

  • 修改config.ini中CommonAddr=100(默认为1)
  • 或在 JSON 模板中显式设置"commonAddr": 100
  • 验证:用 Wireshark 抓包,确认发出的 ASDU 中commonAddr字段值是否匹配主站要求(IEC 104 标准规定该字段为 2 字节,大端序)

5.3 现象:发送遥控命令(ASDU=45)后,主站返回COT=46(未执行),但现场设备实际已动作

原因:主站将COT=46(未执行)误当作最终结果,而忽略后续COT=5(自发上送)的状态更新
解决:

  • 在config.ini中添加WaitForCOT5=true(部分版本支持)
  • 或改用脚本连续发送:先发COT=6(激活),再立即发COT=5(状态上送),强制主站关联两次报文
  • 根本方案:要求主站厂商修复 COT 映射逻辑,COT=46应仅表示“命令已接收”,非“执行失败”

5.4 现象:启用UseTLS=true后,TCP 连接成功,但日志显示SSL handshake failed

原因:主站使用 TLS 1.3,而工具内置libtls_wrapper.dll仅支持 TLS 1.2
解决:

  • 主站侧降级 TLS 至 1.2(推荐)
  • 或替换libtls_wrapper.dll:从 OpenSSL 1.1.1t 编译新版 DLL(需 VS2019+ CMake),替换时确保libcrypto-1_1.dll和libssl-1_1.dll版本匹配
  • 紧急绕过:用stunnel做 TLS 代理,工具连127.0.0.1:2405,stunnel转发至主站192.168.1.100:2404并处理 TLS

5.5 现象:同一台电脑,昨天能连,今天突然TCP: Connection refused

原因:Windows 更新后重置了localhost解析,或主站进程崩溃后端口未释放
解决:

  • 运行netstat -ano | findstr :2404,若 PID 存在但无对应进程,用taskkill /F /PID XXXX强杀
  • 检查hosts文件(C:\Windows\System32\drivers\etc\hosts)是否新增了127.0.0.1 iec104-test类条目,导致 DNS 解析异常
  • 终极验证:用telnet 192.168.1.100 2404测试端口连通性,若 telnet 通则必为工具问题;不通则为主站或网络问题

6. 进阶技巧:用报文重放功能复现偶发故障,把“玄学问题”变成可验证 Bug

6.1 从日志提取故障报文:精准定位“一次性的丢包”

现场常遇到“主站偶尔收不到某次遥控”,但无法复现。此时需用工具的报文重放功能:

  1. 在logs/中找到故障时段日志(如20240520_142315.log)
  2. 复制故障前后的完整 TX/RX 交互(至少包含 STARTDT、ASDU、S 帧)
  3. 新建replay_fault.json,按以下结构组织:
{ "sequence": [ { "direction": "TX", "hex": "680407000000" }, { "direction": "RX", "hex": "68040B000000" }, { "direction": "TX", "hex": "680D0000000001010001000101" } ] }
  1. 运行重放脚本:
    python scripts/replay.py replay_fault.json --interval 1000
    --interval 1000表示每帧间隔 1 秒,模拟真实时序

为什么有效:

  • 真实故障常由主站 CPU 突增、数据库锁表、或网络抖动引起,单次重放无法触发
  • 用--loop 100参数循环 100 次,配合--jitter 50(±50ms 随机延迟),可高概率复现时序敏感型 Bug

6.2 构造边界报文:测试主站对非法字段的容错能力

IEC 104 标准允许infoAddr最大为 65535,但某些主站解析库用int16存储,导致infoAddr=65536时溢出为0。用脚本构造越界报文:

# 构造 infoAddr = 65536(超出 uint16 范围) asdu_json = { "type": 1, "vsq": {"sq": False, "num": 1}, "cot": 20, "commonAddr": 1, "infoAddr": [65536], # 关键:故意越界 "data": [{"value": 1}] }

预期结果:

  • 合规主站应拒绝此报文(返回COT=47:未知类型)
  • 有缺陷主站可能崩溃、重启,或静默丢弃——这正是你需要提交给厂商的 Bug 证据

6.3 与主站日志对齐:用时间戳建立报文因果链

工具日志时间精度为毫秒,主站日志常为秒级。为精准对齐:

  1. 在工具发送前,记录系统时间:
    import time start_time = time.time() * 1000 # 毫秒级 send_asdu() # 发送报文 print(f"[{start_time:.0f}] TX ASDU sent")
  2. 主站日志中搜索该毫秒时间戳附近的IEC104关键词
  3. 若主站日志无记录,但工具TX日志存在 → 问题在传输层(网络丢包)
  4. 若主站日志有记录但无响应 → 问题在主站应用层(如地址未配置、权限不足)

我干这行八年,最深的教训是:永远不要相信“网络通了就等于协议通了”。IEC 104 的坑,90% 在协议栈的灰色地带——那些标准没写死、厂商各搞一套的地方。这个 ZIP 包的价值,不是帮你省时间,而是给你一把刀,亲手剖开那些“应该工作”的黑匣子,直到看见血肉。
希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 7:43:18

TPS259483与STM32F746ZG实现智能电源路径保护与热插拔控制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:43:13

TPS259483 eFuse与STM32G431组合:工业电源路径保护方案全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:43:12

Unity角色换装Mesh合并核心技术解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:42:54

微信小程序云开发菜谱源码:免服务器搭建完整流程与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:42:03

QT/C++开发俄罗斯方块:从核心算法到课设答辩完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华