ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

吃透IEC104协议:从学习版源码到电力规约开发实战

吃透IEC104协议:从学习版源码到电力规约开发实战 简介面向电力自动化学习者的IEC 104协议C语言源码包对应IEC 60870-5-104远动通信标准基于lib60870-C实现包含客户端与服务器端完整代码可用于理解智能电网设备间的数据交换机制。压缩包共111个文件大小仅877KB核心为39个C源文件和32个头文件覆盖应用服务数据单元解析、传输控制、连接管理与超时重传等模块同时附带构建脚本、说明文档、文档配置以及用于TLS加密通信的证书密钥文件便于在Linux环境下交叉编译和二次开发。目前已有429人浏览学习。通过细致阅读源码可以掌握报文的编码与解析流程了解TCP/IP Socket编程在电力远动中的应用并可从协议栈中附带的安全传输实现学习为传统规约添加TLS加密的方法。代码包目录结构清晰源码注释较为规范适合作为课程设计或毕业设计的参考资料通过阅读连接管理和定时器实现还能深化对TCP状态机及可靠传输的理解对希望进入电力物联网或智能电网方向的技术人员也是一份难得的实战素材。 做电力自动化的人电脑里没存过一份IEC104源代码都不好意思说自己是干这行的。IEC104协议即IEC 60870-5-104是电力调度远动通信领域绕不开的核心规范它把变电站里的遥测、遥信、遥控数据通过TCP/IP网络送到调度主站。标题里“仅供自己学习使用”这个说法很实在说明你拿到手的不是商用闭源库而是一份能逐行读、能自由修改的学习素材。市面上能跑的IEC104源码不少但大部分要么封装太厚、要么注释太少真正适合用来理解协议本质的反而难得。这篇文章我想从一个实际研读过、改写过IEC104源码的从业者角度聊聊怎么把一份学习版源代码吃透。不会只丢一堆标准文档链接而是直接讲代码里那些关键函数在做什么、为什么这么写、报文在哪一层被组装和拆解以及我自己踩过的坑。适合刚接触电力规约开发的工程师、自动化专业的在校生以及那些准备做电网协议适配但又不知道从哪下手的同学。1. 为什么一份“学习版”IEC104源码值得逐行读1.1 源码是最快的协议学习路径很多人学IEC104第一反应是去啃IEC 60870-5-104标准原文或者买一本电力规约的书从头看到尾。说实话这个路子效率很低。标准文档为了保证严谨性术语密度极高一句话能拆出七八个限定条件读完了脑子里还是一团浆糊。代码不一样代码是把协议标准翻译成机器逻辑的最终产物每一个结构体、每一个switch分支、每一次memcpy都是对标准条款的一次具体实现。你对着源代码去反推协议条款远比对着条款去想象代码怎么写要轻松得多。而且“仅供自己学习使用”的代码通常意味着作者没有做过度封装。商用库为了通用性会抽象出各种层、各种回调、各种线程模型看半天都不知道报文在哪一刻被拼出来的。学习版代码一般就是直线思维——收包、解析、处理、组包、发送一条流走到底这种代码最适合用来建立协议的整体认知。我手上的这份源码大概三千多行核心文件也就几个一个周末认真捋一遍就能把主链路串起来。1.2 拿到源码后先别急着编译先把工程翻一遍这是我反复强调的习惯拿到学习版代码第一件事不是make也不是点IDE里的运行按钮而是先把文件列表完整看一遍。一个规范的IEC104源码工程通常会有这样几个模块链路层处理socket收发、TCP连接管理、APCI解析启动、停止、测试帧、ASDU编解码不同类型报文的组装与拆分、应用层逻辑总召处理、遥信变化上报、遥控命令执行。你不需要记住每个文件的每一行但要在心里画出一张地图——哪个文件负责收数据哪个文件负责解析报文哪个文件对应四遥里的哪一类。这一步的价值在于后续你调试问题时能快速定位。比如发现遥测数据上不去你就知道该去ASDU解码那块找如果链路反复掉线说明是TCP保活或者序号校验的逻辑出了问题。没这份地图你只能一个文件一个文件地打断点效率低到让人想摔键盘。2. IEC104协议的核心机制与源码里的对应关系2.1 先认识一下APDU协议的最小工作单元IEC104所有的通信本质上都是在传输APDU应用协议数据单元。每个APDU由APCI应用协议控制信息和可选的ASDU应用服务数据单元组成。源码里一般会有一个结构体比如叫iec104_apdu_t里面包含起始字节、长度、控制域以及紧跟其后的一段数据缓冲区。固定起始字节是0x68第二个字节是后面内容的长度这两个字段几乎是所有IEC104代码里第一个被检查的东西。我自己刚上手时犯过一个很低级的错误以为收到的每个TCP包就是一个完整的APDU。实际上TCP是流协议一次recv可能拿到半个APDU也可能拿到三个半APDU。学习版源码通常会在这一层做缓冲处理把收到的数据累积到一个环形缓冲区或者动态缓冲区里然后循环检查里面有没有完整的APDU。这个处理逻辑值得重点看因为很多实际运行中的通信问题根源就是拆包粘包没处理好。// 伪代码示意缓冲区中提取完整APDU while (buf_len 2 buf[0] 0x68) { int apdu_len buf[1] 2; // 长度字段不含起始字节和长度字段自身 if (buf_len apdu_len) break; // 数据不完整等下一次收包 process_apdu(buf, apdu_len); buf apdu_len; buf_len - apdu_len; }理解这段逻辑后再看TCP收发线程就会通透很多。学习版代码里收发通常各有一个线程或者用select/epoll驱动但不管哪种模型缓冲区处理这块都是共通的。2.2 控制域里的序号机制U帧、S帧、I帧IEC104的控制域只有一个字节或者四个字节区分三种帧类型U帧编号0x03、0x23等、S帧0x01开头、I帧序号从0开始递增。学习源码时我最建议先把这三个宏定义找出来U帧控制域为0x07表示STARTDT act0x0B表示STARTDT con0x13表示STOPDT act0x23表示STOPDT con0x43表示TESTFR act0x83表示TESTFR con。S帧控制域第一个字节低两位为01用于确认对方发送的I帧自身不带数据。I帧控制域前两个字节是发送序号后两个字节是接收序号真正承载ASDU数据。有生活化类比可以帮助理解链路建立后的首次通信很像打电话前的“你在吗我在呢”。U帧就是这种链路控制信号负责建立和维持会话S帧是“我收到你说的了”的纯确认I帧才是真正聊天内容。序号机制则是双方各自维护一个计数器防止消息丢失或重复。源码里序号处理那段建议重点关注取模运算。因为IEC104的序号是12位范围0到4095超出后回绕。很多学习版代码会用(send_seq 1) % 4096这种写法简单有效。但要注意真实场景里可能同时存在多个未确认的I帧所以接收窗口大小的管理也是一个考点。学习版代码通常不会实现完整的滑动窗口但至少会记录recv_seq和send_seq两个变量调试时打印这两个值基本就能定位数据不同步的问题。2.3 四遥报文的解析思路ASDU才是真正干活的ASDU是IEC104里最贴近业务的部分学习版源码的核心工作也大多集中在这里。一个ASDU包含类型标识、可变结构限定词VSQ、传送原因COT、公共地址厂站地址和信息体。类型标识决定了这条报文是遥信如0x01单点遥信、遥测如0x09短浮点遥测、遥控如0x2D单点命令还是总召0x64。拿到一份源码后可以先画出这样一个对照表把所有支持的类型标识整理出来类型标识含义传输方向常见场景0x01单点遥信 (M_SP_NA_1)站端→主站开关位置、保护动作信号0x09短浮点遥测 (M_ME_NC_1)站端→主站电压、电流、有功功率0x0D累计量 (M_IT_NA_1)站端→主站电度表读数0x2D单点遥控 (C_SC_NA_1)主站→站端分合闸命令0x64总召唤 (C_IC_NA_1)主站→站端全数据初始化看ASDU编解码代码时最容易懵的是字节序和数据类型转换。IEC104里信息体地址是三个字节部分老代码会把它们当作int来读但如果主机是大端序就会得到完全错误的值。学习版代码如果考虑了跨平台一般会用移位运算手动组地址比如addr (buf[0] | (buf[1] 8) | (buf[2] 16))而不是直接memcpy。这个细节值得留意因为很多莫名其妙的“地址串位”问题都是字节序没处理好。3. 把代码跑起来编译、配置与链路联调3.1 编译前的准备工作学习版IEC104源码通常依赖libpcap抓包、pthread多线程等基础库。在Linux下先确认环境里装好了gcc、make和这些依赖。Windows下如果用MinGW需要注意socket API的差异不过多数学习版代码已经用条件编译把Winsock和BSD socket的差异处理掉了。sudo apt-get install gcc make libpcap-dev编译过程一般很顺利真正花时间的是配置。源码里通常有一个配置文件或者几个宏定义用来设置本地监听端口、远动站地址、公共地址等。端口默认是2404这是IEC104的标准TCP端口。厂站地址这个参数一定要好好确认它对应ASDU里的公共地址主站和站端不一致时报文会被直接丢弃但你会发现TCP连接是正常的这种问题极难排查。3.2 启动流程到底走哪几步IEC104链路启动是一个固定的六步握手过程。主站侧调度端主动发起TCP连接连接建立后主站发送U帧STARTDT act站端回复STARTDT con然后双方才能正式传输I帧。如果站端认为链路异常可能先发STOPDT act等收到确认后再重新走STARTDT流程。测试帧TESTFR用于链路保活周期性地确认对方还活着。我在源码里定位这六步时一般直接搜控制域的宏定义然后顺着宏找到处理函数。学习版代码通常会在收到STARTDT act后置一个状态标志比如link_started 1只有这个标志为真时后续收到的I帧才会被继续解析否则直接丢弃。看懂了这段你就明白为什么有时候TCP连接是通的数据却一股都不往上送——很可能就是STARTDT流程没走完或者状态标志被意外重置了。3.3 用模拟器做一次完整链路验证推荐一个我常用的联调方式用IEC104 client simulator作为对端和你的学习代码互通。模拟器可以模拟主站或者站端支持手动下发总召、遥控等命令还能按周期自动发送遥测数据非常适合验证代码的正确性。具体操作可以这样先用模拟器作为主站连接你的站端代码。主站发出STARTDT act观察你的代码是否回复STARTDT con。然后主站下发总召类型标识0x64传送原因6你的代码要能正确解析并回复带数据的总召确认帧传送原因7接着上送全部点位的遥信和遥测。如果模拟器界面上的数据点开始刷新说明整条链路已经打通。这里分享一个心得不要把模拟器窗口放在前台死死盯着。先在代码里加好日志把收发的原始报文按十六进制打印出来再配合Wireshark抓包对比效率会高很多。一条报文从模拟器发出到Wireshark里看到再到日志里输出解析结果三个地方一比对问题出在收、解析、还是发送一目了然。# Wireshark过滤IEC104流量的标准写法 tcp.port 24044. 研读源码时最常踩的坑与排查思路4.1 连接建立了却不传任何数据这是最典型的新手迷惑现场。TCP三次握手已经完成2404端口也通但双方就是“尬聊”。出现这种情况八成是U帧握手没走完。检查你的代码是否在收到STARTDT act之后正确回复了STARTDT con检查主站侧是否主动发起了STARTDT因为有些模拟器默认不会自动启动链路需要手动点一下“启动”按钮。这个坑我在第一次用源码时也踩过当时一头扎进ASDU解析里找原因折腾了一下午才发现是模拟器界面有个启动按钮没点。4.2 总召下发后一条遥信都没回来总召的处理逻辑一般分布在两个地方收到总召命令后的应答以及组织数据上送的回调函数。很多学习版代码为了简化会用一个全局数组模拟遥信遥测的当前值你需要把点位总数、起始地址、每个点位对应的类型标识都配置正确。如果总召命令解析成功但返回的数据长度是0很可能就是点位表为空或者生成报文的循环条件写错了。还有一个容易被忽略的点总召的应答有严格顺序。先回一个总召确认帧传送原因7然后按地址从小到大把所有数据帧发完最后再回一个总召结束帧传送原因10。很多学习版代码只实现了中间的数据帧确认帧和结束帧都没发导致主站认为总召没有执行成功。调试时抓包看三个阶段的帧是否完整能快速缩小问题范围。4.3 控制命令发下去现场没动作遥控命令类型标识0x2D/0x2E和其他报文不太一样它需要带一个限定词QU0x00表示执行0x01表示撤销和一个选择/执行标志S/E。有些学习版代码虽然能收到遥控报文但解析时把S/E字段当成了普通数据忽略掉导致命令内容不完整现场设备自然没反应。排查方法是打印收到的遥控ASDU原始字节对照标准逐字节核对看看限定词和处理标志是不是被正确提取。4.4 收发序号对不上链路反复重建如果日志里频繁出现序号超差的记录并且链路不断重建很可能是在并发环境下序号自增操作没有加锁。IEC104要求每个I帧都带发送序号和接收序号两个线程同时发送报文时如果序号变量不是原子操作就会出现序号跳变、重复等异常。学习版代码里常见的修复方式是在发送函数里加互斥锁或者用__sync_fetch_and_add这类原子操作。这个问题在单线程循环里不会暴露一旦改成多线程发送就立刻现出原形。5. 从“看懂”到“能改”我的学习体会把一份学习版IEC104代码从头到尾捋清楚和能独立修改它中间还有一道坎。我的做法是从小改动开始先给代码增加一个自定义扩展报文比如把设备厂商信息打包进一个自定义ASDU发给模拟器验证再把原本写死的点位表改成从外部配置文件加载每次启动时读取点位定义。这些小改动覆盖了组包、解析、配置加载、状态管理等多个环节每完成一个对协议的理解都会深一层。在后续扩展时有几个方向值得尝试把这套代码移植到ARM嵌入式平台比如RK3506这类工业级芯片上或者用Python重新实现一个简化版IEC104客户端用于自动化测试再或者为它加上TLS加密满足电力监控系统网络安全防护的要求。这些方向每一条都能延伸出不少实战经验但前提都是先把基础协议玩透。最后再分享一个个人习惯我会把研读过程中画的协议流程图、报文交互时序、各结构体字段含义整理成一份自己的笔记连同源码的注释一起提交到git仓库。三个月后再回来看这份笔记比任何标准文档都更有价值因为它是你自己踩过坑、填过洞之后沉淀下来的理解。学习版IEC104源码只是起点真正值钱的是你从源代码里长出来的那一整套调试思路和工程直觉。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进