
简介面向电力自动化领域开发者的C语言IEC 60870-5-104规约实现源码包基于TCP/IP网络通信主要用于远动系统中RTU、保护设备与调度主站之间的数据交换可帮助解决设备远程监控与控制的通信问题。压缩包体积仅11KB共包含3个文件包括1个C源文件、1个头文件和1个txt说明文件结构清晰适合快速阅读与移植。C源文件实现了服务器端从连接建立、报文解析到响应处理的完整逻辑头文件则定义了APDU、ASDU等报文结构以及收发函数接口txt文件提供来源等辅助信息。目前已有360人学习浏览适合电力行业软件工程师、自动化系统开发者以及通信协议学习者作为参考。通过研读并运行这套代码可以深入掌握IEC 104规约的组帧与解析、心跳保活、遥测遥信上传、命令下发与确认、异常处理等关键机制同时由于C语言直接操作内存、贴近底层代码在执行效率和可控性上具备明显优势既能作为实际项目的开发蓝本或移植基础也能用于对照标准逐行学习理解规约设计精髓。 “博主我下了一个 iec104.rar解压密码是多少”这是我这半年收到频率最高的一类提问。问的人多了我觉得有必要把话说透一个以iec104.rar命名的压缩包八成就和电力调度、变电站自动化里最常见的 IEC 60870-5-104 协议简称 IEC 104有关里面装的要么是协议说明文档要么是厂家提供的调试工具要么是过来人整理的学习例程。而绝大多数人之所以卡在第一步根本不是解压密码的问题而是压根不清楚这个包里装的是什么东西、该在什么环境里跑、拿到手之后怎么验证它能不能用。这篇文章不打算围绕“怎么找密码”这种歪路展开。我会从一个来路不明的压缩包说起讲清楚 IEC 104 协议学习/开发中真正值钱的那几条经验如何安全地处理网上下载的资源包、如何快速理解协议核心概念、如何不依赖那些被改得面目全非的共享工具自己动手搭一套能用的主站和从站测试环境以及我在实际联调中踩烂过的几个坑。无论你是刚入行的电力自动化工程师还是半路转过来做工业通信开发的这篇都应该能帮你省下不少瞎折腾的时间。1. 先说清楚你下载的 iec104.rar 里到底有什么1.1 常见内容物模拟器、协议库、说明文档、历史工程我把手头收集到的几十个同类压缩包拆开看过里面装的其实就四类东西。第一类是“客户端/主站模拟器”比如某些厂家出的调试软件、教学演示程序这类程序一般要安装 .NET 或 Java 运行环境打开界面后可以填 IP、端口、公共地址然后能发总召唤、读遥信遥测。第二类是“从站模拟器”用来模拟一个 RTU 或保护装置方便主站开发人员在没有真实设备时做联调。第三类是协议开发包常见的有 C/C、C#、Java 的封装库里面会带几个示例工程和 API 文档。第四类最杂可能是某次工程的配置文件、抓包文件甚至是某个学生毕设的整套源码命名随意目录混乱但里面的报文记录却往往最真实。老实说除了一眼就能认出的官方工具安装包其他几类我都不建议你直接在生产环境里用。原因很简单你无法确认这个包有没有被人改动过也无法确认它的协议实现是否严谨。网上流传的很多“IEC 104 客户端模拟器”界面做得像模像样实际测试时会发现它连基本的 S 帧确认时序都处理不对拿它去验证自己的主站程序只会把你带进沟里。1.2 为什么很多人解压完就跑不起来我见过太多人卡在这一步。压缩包好不容易解开了双击 exe 没反应或者弹窗报错“缺少 DLL”“找不到 Java 环境”“运行时错误”。于是第一反应就是到处找人要补丁、要破解码。其实大多数情况下问题根本没有那么玄。最常见的三个原因一是运行环境不对64 位系统装了个 32 位的老工具或者反过来二是缺少 VC 运行库、.NET Framework、Java JRE 这类基础依赖三是配置文件里写的通信参数和实际场景对不上程序起来了但连不上设备看起来像“没破解成功”。我自己处理这类包的标准动作是先看压缩包里的readme.txt或说明.txt再确认自己机器上装了哪些运行环境然后在隔离目录里运行观察进程和网络端口状态。很多所谓“跑不起来”的工具其实只要补上运行库就能正常工作根本不需要什么注册机。1.3 关于解压密码和破解补丁这条捷径真的不能走把话说得直接一点当你开始在网上搜索“破解码”“密码破解”的时候你就已经站在一个非常危险的入口了。这类资源包大多数来自不明渠道如果作者故意加了密码通常意味着他不希望内容被随意扩散。强行破解密码、使用注册机往轻了说是违反软件授权协议往重了说你下载的那个“破解补丁”本身可能就是木马。我在测试环境里解压过两个标着“破解版”的工具杀毒软件当场报出后门程序。你想想工业自动化领域的工具拿它去连真实的变电站设备或调度系统如果真的被植入恶意代码后果根本不是“电脑中毒”那么简单。所以我的建议是遇到带密码的压缩包第一反应不应该是找破解手段而是问自己——这个工具我是否真的需要有没有开源替代品下面的章节我会给出更靠谱的方案。2. 上手 IEC 104 之前先把这几个核心概念弄明白2.1 传输层TCP 2404 与 APCI 帧格式IEC 104 协议的规定很明确使用 TCP 传输默认端口是 2404传输层之上是 APCI应用协议控制信息再往上才是业务数据 ASDU。为什么要知道 TCP 端口因为你要在防火墙上放行它要用 Wireshark 抓包过滤它也要在代码里写对它。很多时候主站连不上从站查来查去最后发现是从站监听端口写成了 2405这种低级错误在真实项目里一点都不罕见。APCI 帧分三种I 帧承载数据S 帧做确认U 帧做启停控制。I 帧里有两个重要的序号 N(S) 和 N(R)用来保证报文不丢不重。你要是自己用 Socket 收发报文必须正确处理这两个序号否则对方会把你的报文当作重复帧丢弃。这也是很多初学者自己写协议栈时最容易懵的地方。2.2 应用层ASDU 的结构和常见类型ASDU 是真正承载业务数据的部分由类型标识、可变结构限定词、传输原因、公共地址、信息对象地址和数据域组成。你不需要背下所有类型标识但下面这几个高频类型一定要认识总召唤类型标识 100、时钟同步103、单点遥信1、双点遥信3、遥测9/11、单点遥控45、双点遥控46、设点命令48/49/50。以最常见的单点遥信为例一个信息对象地址对应现场一个开关位置值为 0 表示分1 表示合后面还要跟着品质描述符。很多初学者只看值不看品质位结果把“无效”的遥信当成真实状态处理这种问题在电力系统里是致命的。你拿网上那些模拟器跟自己对点的时候一定要留意报文中品质描述符的变化。2.3 联调时最容易翻车的参数匹配问题主站和从站明明都启动了也在同一个网段里TCP 也连上了但就是收不到数据。这种时候 90% 是参数没对齐。我按踩坑频率排个序端口号不匹配、公共地址Common Address不一致、信息对象地址范围对不上、总召唤的传输原因或限定词处理不对。公共地址是最容易出问题的。主站配置里写的是 1从站里写的是 65535两边 TCP 都通着但 ASDU 一到对方就被当成非法报文扔掉。这种问题抓包都很难看出来因为你抓到的包表面上看结构完整实际上地址域已经错了。所以联调的第一步永远是核对参数表别上来就怀疑协议栈有 Bug。3. 从下载到部署我处理这类压缩包的标准流程3.1 解压前的安全检查不管这个压缩包是从哪个群里、哪个网盘链接下的我的第一个动作永远是把它放到一个单独的目录然后用杀毒软件做全量扫描。这一步不能省哪怕文件是熟人发来的。为什么因为压缩包里的文件可能被二次打包熟人也未必知道里面被塞了什么东西。扫描通过之后再看压缩包的注释和文件列表。如果一个标着“IEC 104 工具”的压缩包里出现了一些明显和主题无关的 exe、scr、bat 脚本或者文件名像setup.exe而实际上不来自官方我会直接放弃这个包。宁可不省这个事也不要拿自己的开发机冒险。3.2 解压后的目录结构与文件鉴别解压完成之后别急着双击任何可执行文件先把目录结构过一遍。正常工具的目录里会有bin、lib、doc、sample这样的子目录如果是源码包会有工程文件、头文件、源文件如果是协议资料包一般就是 PDF、Word 加几个抓包文件。看到单独一个孤零零的iec104.exe躺在根目录里没有任何配套文件这种反而要警惕。我会重点看两个文件一是readme里面通常会写环境要求、配置步骤、版权声明二是配置文件比如.ini、.xml、.config从里面能读出默认端口、默认地址、日志级别这些关键信息。通过这些文件基本就能判断这个工具是不是可用的、适不适合我当前的环境。3.3 运行环境与依赖补齐这一步就是“跑不起来”的解决专场。我先看工具是 .NET 写的还是 Java 写的是 32 位还是 64 位然后检查本机环境。Windows 上最容易缺的是 VC RedistributableJava 工具容易缺的是 JRE 版本不匹配有些老工具甚至需要特定版本的 JDK 才能启动。补齐依赖之后再运行程序能弹窗了但连设备还是失败。这时不要慌占住“先看日志、再抓包、最后改配置”的顺序来。很多工具自带日志文件里面会明确告诉你哪一步失败抓包能确认 TCP 握手是否成功、是否有数据包来回如果 TCP 都是通的但应用层没数据才需要去核对公共地址和端口配置。这一套流程走下来80% 的问题都能自己解决。4. 不折腾来路不明的模拟器了自己搭一套主站和从站4.1 开源协议栈选型对比如果你只是要验证一个简单的转发逻辑或者要做自动化测试我强烈建议你放弃“找模拟器”的思路改用开源协议栈自己写一个。主流选择有三个lib60870C/C应用最广、j60870Java、libiec61850里附带的部分 104 支持不太推荐还是专注 61850 为主。其中 lib60870 用的人最多文档相对齐全而且支持 Linux 和 Windows 交叉编译。选型的时候不要看谁代码写得漂亮要看三个点是否支持主站和从站两种角色、是否带回调机制方便做二次开发、示例工程是否完整。lib60870 的 examples 里既有server也有client直接把示例跑起来就能有一个能用的主站/从站比你在网上找那些乱七八糟的模拟器靠谱得多。4.2 基于 lib60870 的最小从站示例从站的基本逻辑是监听 2404 端口等主站连上来收到总召唤后上报遥信遥测收到时钟同步后校正时间。lib60870 把这些底层细节都封装好了你要做的就是设置信息对象地址绑定回调函数。#include iec60870_common.h #include iec60870_server.h static int reportSinglePoint(CS101_ASDU asdu, int address, bool value) { InformationObject io (InformationObject)SinglePointInformation_create( address, value, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(asdu, io); return 1; } static void connectionRequestHandler(void* parameter, IMasterConnection connection) { printf(主站接入\n); if (IMasterConnection_sendASDU(connection, asdu) false) { printf(发送失败\n); } } int main() { sIEC60870_Server server IEC60870_Server_create(2404, 10); IEC60870_Server_setConnectionRequestHandler(server, connectionRequestHandler, NULL); IEC60870_Server_start(server); while (1) sleep(1); }这段代码里最关键的是回调函数connectionRequestHandler主站发什么请求都会走到这个回调里来。你在回调里根据 ASDU 的类型标识判断是总召唤还是时钟同步分别返回不同的数据组。第一次接触的人很容易漏掉总召唤响应是必须要在回调里明确处理的不是说你起个 TCP 服务就完事了。4.3 基于 lib60870 的最小主站示例与报文验证主站的逻辑相对更简单连接从站发送总召唤等待遥信/遥测上报。#include iec60870_common.h #include iec60870_client.h static void asduReceivedHandler(void* parameter, CS101_ASDU asdu) { if (CS101_ASDU_getTypeID(asdu) M_SP_NA_1) { int count CS101_ASDU_getNumberOfElements(asdu); for (int i 0; i count; i) { SinglePointInformation io (SinglePointInformation)CS101_ASDU_getElement(asdu, i); printf(地址 %d 状态 %d\n, SinglePointInformation_getObjectAddress(io), SinglePointInformation_getState(io)); } } } int main() { sIEC60870_Client client IEC60870_Client_create(); IEC60870_Client_setASDUReceivedHandler(client, asduReceivedHandler, NULL); IEC60870_Client_connect(client, 127.0.0.1, 2404); sleep(1); IEC60870_Client_sendInterrogationCommand(client, CS101_COT_ACTIVATION, 1, 0); sleep(5); IEC60870_Client_destroy(client); }写完主站从站后不要急着对接真实设备先用本机 127.0.0.1 联一下能用 Wireshark 抓到完整的总召唤和响应过程就说明你的基础链路是通的。抓包时过滤条件写tcp.port 2404重点看三样东西TCP 握手是否正常、是否有 U 帧启动STARTDT、总召唤请求后是否有 I 帧数据返回。这三样都正常协议栈基本就没问题了。5. 联调实测中反复踩到的 4 个坑5.1 总召唤时序总召唤不是想发就发的。从站收到总召唤后要立刻响应但在响应之前它会先确认你这条命令有效然后才把当前所有信息对象一口气发过来。如果你在主站里发完总召唤之后马上就去查数据这时候数据还没到位十有八九会读到空的结果。我踩过的坑是用开源库发总召唤回调里解析到了“总召唤确认”误以为这就是全部数据了结果后续的 ASDU 没有等程序直接进入下一循环导致遥信数据永远少一帧。正确做法是总召唤分为“激活确认”和“数据上报”两个阶段必须在收到“激活终止”之后才认为这轮总召唤结束。5.2 公共地址不匹配导致的静默丢弃这个问题我前面提过但值得单独拿出来再强调一遍因为它在联调现场出现的概率太高了。很多厂家的设备配置文件里有两个地址一个是站地址一个是公共地址。你以为填了其中一个就行实际上 IEC 104 的 ASDU 里带的是公共地址站地址那个版本里未必对应。现场实测最容易遇到的现象是TCP 已经连上了主站发总召唤从站的日志里也显示收到了消息但就是不回数据。排查到最后往往是公共地址不匹配从站把这条 ASDU 当成发给别的站的报文直接丢弃了。所以在代码里打日志的时候一定要把CS101_ASDU_getCommonAddress打出来两边一对比就知道问题在哪。5.3 品质描述符与遥测死区遥信报文里的品质描述符QDS是很多人容易忽视的字段。它的每一位都代表不同的含义比如0x01表示“已被取代”0x02表示“存在溢出”0x20表示“无效”。我用 lib60870 测试过一次故意把品质位置成0x20结果主站那边因为自作聪明地把无效值当成了 0导致界面上显示所有开关都是分位。后来翻了规范才发现无效品质位必须当作异常处理不能直接显示成正常状态。遥测死区则是另一个问题很多从站会配置一个死区值比如 0.5%只有模拟量变化超过这个阈值才主动上报。如果你觉得“数据怎么老是不刷新”先别怀疑网络去查死区配置是不是设得太大。5.4 网络隔离与防火墙策略IEC 104 本身是明文协议没有任何加密和认证机制。这意味着只要有人能接入你的二层或三层网络他就能直接读取、伪造甚至下发控制命令。开工控项目的同学一定要把这条刻在脑子里不要因为开发方便就把 2404 端口直接暴露到公网或办公网。我自己的做法是开发测试环境放在隔离网段调试机上只对固定的对端 IP 开放 2404同时在防火墙里把入站访问控制配好。如果是跨区域联调优先走专网并在网络接入设备上做白名单。控制类业务一定要慎之又慎宁可慢一点也不要在安全上给自己埋雷。最后补一句我自己的实操习惯。我现在已经不指望网盘里那些来路不明的iec104.rar了需要测试环境的时候都是直接用开源协议栈起服务配置文件全部用文本管理改完参数跑一遍回归脚本异常报错全在日志里。这样哪怕三个月后再翻出当时的工程我也能根据日志和配置快速还原现场。你如果也经常做电力自动化联调不妨试试这个思路把依赖外部工具的习惯改成依赖协议规范和源码可控的自建环境。刚开始会慢一点但越往后越省心。本文还有配套的精品资源点击获取