
简介电力系统IEC104规约典型报文应用解析.pdf是一份面向电力系统调度通信及自动化运维人员的专业参考文献聚焦IEC104规约在变电站双平面接入调控中心场景下的实际应用。文档系统梳理了TCP/IP传输模式、平衡式通信、默认2404端口等基本规则以及I帧、S帧、U帧三种帧格式并给出了测试U帧、总召I帧和确认S帧等典型报文的字节级解析。同时结合国网新规范讲解规约的OSI五层结构、主动传输与问答相结合的机制为协议调试、系统开发和故障排查提供直接参考。压缩包内共1个PDF文件大小约1.69MB内容精炼、结构清晰。目前已有402人学习下载适合电力系统相关工程师、研发人员及高校相关专业学生作为专业指导。1. IEC104典型报文为什么值得逐字节读一次调度对点调试的真实痛点电力后台和调度对点最怕的不是数据库配错而是主站那边一句“链路中断”这边抓包一看全是控制帧在飞业务报文一条都没有。IEC104规约的典型报文翻来覆去就那几十种但真正决定你能不能把问题定位到“配置错”还是“时序错”的往往就在几个关键字节上。这篇笔记按现场调试的顺序把IEC104规约的典型报文从帧结构、类型标识到总召唤、时钟同步、遥信、遥控的十六进制实例拆一遍再把我踩过的坑和排查思路放出来。适合刚接手104调试的二次检修、测试工程师也适合被对点问题折腾过、想系统补一课的人。2. IEC104规约报文骨架APCI控制域与ASDU信息体的读法2.1 从0x68开始读起始字节、长度字与APDU边界任何一条IEC104报文抓包看到的都是完整APDU应用协议数据单元。APDU由两部分组成APCI应用协议控制信息和ASDU应用服务数据单元。APCI固定6个字节ASDU长度不定。第一条规则丢到眼前的十六进制先找0x68。0x68是APDU的起始字节第二个字节是长度字表示从第三个字节起到报文末尾一共有多少字节。比如68 0E开头的报文0E换成十进制是14说明后面还有14个字节这条APDU总长就是16字节1个起始字节1个长度字14个数据字节。这个长度字在调试里非常有用。常见坑是抓包软件把TCP分段截断导致一条APDU被拆成两三个TCP包。看长度字能判断当前这段数据是不是完整的一条报文。我一般先在Wireshark里过滤tcp.port 2404然后按“长度字1”去对数数据对不上就先怀疑抓包分段而不是怀疑从站没发。2.2 I帧、S帧、U帧三类控制帧的判定与切换关系APCI的第三到第六个字节是控制域共4字节。控制域低两位的取值决定了帧类型帧类型控制域低两位作用常见hex开头I帧00携带ASDU的数据帧带发送序号和接收序号68 0E 00 00 00 00S帧01纯确认帧不带ASDU只回接收序号68 04 05 00 00 00U帧11链路控制帧用于启动、停止、测试68 04 07 00 00 00U帧最常见的是启动握手。主站建立TCP连接后第一条必须是U帧STARTDTact启动数据传输激活hex是68 04 07 00 00 00。从站收到后回STARTDTcon启动确认hex是68 04 0B 00 00 00。这一步没完成后面所有I帧都不会被处理。我见过不少现场主站TCP连接都建立了但直接发总召唤从站根本不回问题就出在少了这个U帧握手。S帧只有4字节的APCI没有信息体。它的作用就是告诉对端“你发的I帧我收到了”。S帧里带接收序号不带发送序号。I帧同时带发送序号和接收序号既发数据又确认对方的数据。2.3 ASDU结构类型标识、传送原因、公共地址与信息体地址ASDU从APDU的第七个字节开始也就是控制域后面的位置。ASDU前6个字节是描述头后面是信息体。类型标识1字节告诉接收方这条报文是什么类型。总召唤是0x64时钟同步是0x67单点遥信是0x01带时标的单点遥信是0x1E单点遥控是0x2D双点遥控是0x2E浮点遥测是0x0D。这个字节决定后面信息体的解析方式第一眼必须认出来。可变结构限定词VSQ1字节描述信息体数量。它的最高位bit7表示是连续寻址还是单个寻址bit7为0表示连续寻址信息体地址只写第一个后面的递增bit7为1表示单个寻址每个信息体都带完整地址。低7位是信息体个数。注意0x81表示“1个信息体单个寻址”不是“81个信息体”这个误读我见得太多了。传送原因2字节低字节在前说明这条报文是主动上送、还是对命令的确认。0x06是激活0x07是激活确认0x0A是激活终止0x03是突发变位上送0x14是响应站召唤。总召唤时从站先回0x07确认中间数据帧的传送原因是0x14最后回0x0A结束。公共地址2字节是站地址信息体地址3字节是具体点位地址。多字节字段都是低字节在前01 00代表地址1不是00 01。3. 典型报文逐个拆总召唤、时钟同步、遥信变位、遥控的十六进制实例3.1 总召唤报文0x64类型标识与召唤限定词0x14总召唤是主站下发的最常见命令用于让从站把全部遥信、遥测刷新一遍。完整报文如下68 0E 00 00 00 00 64 01 06 00 01 00 00 00 00 14逐字节拆解字节位置内容含义0-168 0E起始字节长度142-500 00 00 00I帧发送序号0接收序号0664类型标识100总召唤701VSQ1个信息体8-906 00传送原因6激活10-1101 00公共地址112-1400 00 00信息体地址01514召唤限定词20表示总召唤召唤限定词0x14是关键。如果这里不是20从站不会当作总召唤处理。有的主站配置软件会把召唤限定词写成0x00或者0x01从站那边完全没有反应抓包看又确实发了命令这种错最耗时间。总召唤的正常响应分三步从站先回0x64类型、传送原因0x07的激活确认然后大量I帧上送遥信遥测传送原因都是0x14最后回一条0x64类型、传送原因0x0A的激活终止。我判断总召唤是否闭环就看有没有0x0A结束帧。很多厂家从站的结束帧不是独立一条而是跟在最后一批数据后面但规范上必须有一条0x0A或类似终止标志否则主站会一直等。3.2 时钟同步报文0x67与CP56Time2a七字节时标的生成对时是变电站里天天跑的报文。主站下发时钟同步类型标识0x67信息体地址为0信息体是7字节的CP56Time2a时标。完整报文68 14 04 00 00 00 67 01 06 00 01 00 00 00 00 00 F4 01 1E 0A 1F 07 18控制域04 00 00 00表示这是I帧发送序号1接收序号0。ASDU部分从0x67开始类型标识1031个信息体传送原因6激活公共地址1信息体地址0然后F4 01 1E 0A 1F 07 18是时标。CP56Time2a的7字节顺序是毫秒低字节、毫秒高字节bit7是IV无效标志bit0-5是毫秒高位、分、时、日、月、年。上面例子里F4 01表示毫秒5000x01F45001E是30分0A是10时1F是31日07是7月18是24年表示2024年。时钟同步报文最容易出的问题是IV位。CP56Time2a第二个字节的bit7如果置1表示时间无效。主站下发的时间如果本身没同步IV位为1从站收到后拒绝采纳然后反复上送对时请求主站那边看到的就是“对时频繁失败”。排查时先把时标字节一个个拆开看IV位有没有被错误置1。3.3 遥信变位与SOE0x01与0x1E的差异普通遥信变位上送用类型标识0x01单点遥信信息体1字节0表示分1表示合。带时标的遥信变位SOE用类型标识0x1E信息体在单点值后面多跟7字节CP56Time2a时标。一条典型的单点变位上送68 12 04 00 04 00 01 02 14 00 01 00 01 00 00 00 02 00 00 01拆开看控制域04 00 04 00表示I帧发送序号1接收序号1。类型标识0x01VSQ为0x02表示2个信息体且连续寻址传送原因0x14是响应站召唤公共地址1。第一个信息体地址01 00 00值为00分第二个信息体地址02 00 00值为01合。注意连续寻址时信息体地址只写第一个地址01第二个地址02是自动递增的。如果改成单个寻址VSQ变成0x82两个信息体都要写完整地址报文会明显变长。现场调试时如果发现从站上送的报文里信息体地址是乱的先看VSQ的连续/单个标志位是不是和从站配置一致。SOE的报文结构和普通遥信类似区别在于每个信息体多了时标。调试对点最关心SOE的时标精度看的是从站打时标的时刻而不是主站收到报文的时刻。抓包时用Wireshark能看到报文到达时间但那是网络层时间不能拿来评判SOE准不准这个区别很多新手会混淆。3.4 遥控报文从选择到执行双点命令0x2E的控制字节遥控分单点和双点。单点遥控类型标识0x2D双点遥控0x2E。变电站里断路器、隔离开关大多用双点。双点命令的信息体是1字节的控制字节bit0-1表示分合状态00分01合bit7表示选择还是执行1选择0执行。标准遥控流程是“选择-执行-结束”。选择合闸的报文68 0E 08 00 04 00 2E 01 06 00 01 00 01 00 00 81选择信息体地址01 00 00控制字节0x81bit71选择bit01合。从站回选择确认后主站再发执行68 0E 0C 00 04 00 2E 01 06 00 01 00 01 00 00 01执行控制字节0x01bit70执行bit01合。执行确认返回后主站发结束命令控制字节还是0x01传送原因改成0x0A。遥控最容易翻车的地方是控制字节的bit7写反。有些主站程序会把选择写成0x01、执行写成0x81从站那边收到选择命令直接拒绝遥控就是合不上。抓包时看到选择命令的控制字节先看bit7这是第一判断。4. 抓包与模拟调试用Wireshark和Python从站复现典型报文流4.1 用Wireshark过滤IEC104过滤条件、显示列与时延测量IEC104走TCP端口2404抓包过滤最简单的是tcp.port 2404如果Wireshark识别出IEC104协议直接用过滤条件iec104也行。但我一般还是按端口过滤因为调试时经常遇到从站把104和101混跑或者端口被改过按端口过滤能看到更多上下文。显示列建议加上“TCP Seq”和“Len”方便对照TCP分段。判断一条APDU是否被TCP分包看Wireshark里Len字段和报文长度字是否一致。如果一条IEC104报文的长度字是14但TCP Len只有10说明这条报文被拆到了下一个TCP包里需要打开“Allow subdissector to reassemble TCP streams”选项才能看到完整报文。时延测量用Wireshark的“统计-TCP流图”最直观。选一条总召唤报文作为起始点看从站回第一条数据帧的时间间隔。间隔超过1秒就要怀疑从站CPU负载或数据库扫描太慢间隔只有几毫秒但数据帧数量少就要怀疑从站上送的数据类型没配全。4.2 最小模拟环境用Python模拟从站回放典型报文流现场没带测试仪的时候我习惯用Python写一个最小从站来验证主站行为。下面是最小可跑通的demo监听2404端口处理U帧握手和总召唤import socket # 最小IEC104模拟从站仅演示握手与总召唤应答不处理完整协议栈 def handle_client(conn): while True: data conn.recv(1024) if not data: break print(recv:, data.hex()) # STARTDTact 68 04 07 00 00 00回STARTDTcon 68 04 0B 00 00 00 if data bytes.fromhex(680407000000): conn.send(bytes.fromhex(68040b000000)) # 总召唤类型标识在data[6]0x64表示总召唤 elif len(data) 6 and data[6] 0x64: # 激活确认I帧发送序号0接收序号1 conn.send(bytes.fromhex(680e0000040064010700010000000014)) # 上送两路遥信传送原因0x14地址1分地址2合 conn.send(bytes.fromhex(6812040004000102140001000100000002000001)) # 激活终止I帧发送序号2接收序号1 conn.send(bytes.fromhex(680e0800040064010a00010000000014)) else: # 其他I帧统一回S帧确认N(R)1 conn.send(bytes.fromhex(680405000000)) srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind((0.0.0.0, 2404)) srv.listen(5) print(listening on 2404...) while True: conn, addr srv.accept() print(client:, addr) handle_client(conn)这个脚本把总召唤的完整应答闭环演示出来了激活确认、数据上送、激活终止三件套。代码里有几个关键点。第一U帧判断用整包比对因为U帧没有ASDU直接比对hex最可靠。第二I帧判断用data[6]取类型标识因为APDU第七个字节就是ASDU类型标识这个偏移量在104里是固定的。第三回S帧确认用的是68 04 05 00 00 00接收序号N(R)1对应编码是序号左移2位加上S帧标志位别写成01 00 00 00。许多人在这一步栽跟头。4.3 超时参数配置表t0/t1/t2/t3与端口的现场设定IEC104的链路可靠性依赖四个超时参数不同厂家默认值不一样但调试时按标准值先对齐最稳。参数默认值含义现场经验t030秒TCP连接建立超时网络差的现场调大到60秒否则频繁重建连接t115秒发送确认超时主站发出I帧后15秒内没收到S帧/I帧确认判超时t210秒接收确认超时从站收到I帧后10秒内必须回确认t2必须小于t1t330秒无数据链路测试周期链路上没数据时发TESTFR测试帧保活t2和t1的关系是红线。t2必须小于t1否则从站还没来得及回确认主站就先判超时了。有的现场把t1改成60秒、t2忘了改还是10秒表面没问题但主站重连频繁抓包一看全是超时重发的I帧。调试时先把这个表对一遍能省很多事。端口不一定是2404。有些地区电网要求用其他端口或者通过前置机转换。抓包前先跟主站确认端口号别拿着2404的过滤条件去过滤一个跑在2121端口上的链路的包。5. 排查指南IEC104调试里最常踩的5个坑5.1 控制域序号没左移抓包看序号翻车现象手动解析报文时看到控制域是01 00以为这是序号1但Wireshark里显示序号却是4。原因IEC104的控制域序号是左移2位编码的序号1在字节里是04序号2是08不是01。解决看到00 00是序号004 00是序号108 00是序号2按4递增。手动写报文时先把序号乘4再填进控制域。这个坑在自研主站程序时最容易遇到直接用01 00当序号1发出去从站那边收到的序号不连续直接丢弃。5.2 STARTDT握手永远不成功U帧功能码被搞混现象主站发了68 04 07 00 00 00从站没回68 04 0B 00 00 00链路一直处于“启动中”。原因第一从站应用层没起来TCP连接在但104协议栈没加载第二U帧功能码写错比如把STOPDTact的0x13当成STARTDTact发出去第三防火墙拦了2404端口的入站连接TCP握手能过但数据被拦。解决抓包先看TCP三次握手是否完成再看有没有0x07和0x0B成对出现。只有TCP握手没有U帧问题在从站侧U帧发出去了没有回应问题在从站协议栈或防火墙。5.3 遥信变位不上送VSQ字段被当成数量误读现象从站明明有变位主站就是收不到。抓包发现从站上送的是0x81开头的ASDU有人把它当成“81个遥信”判定数据异常直接丢弃。原因0x81的VSQ最高位是1表示单个寻址低7位的1才是信息体数量。这个报文实际只有1个信息体但很多解析程序只看了低7位“1”没把单个寻址标志处理对导致后续信息体地址解析全乱。解决先把VSQ按“最高位低7位”拆开。确认是单个寻址后每个信息体都要读完整3字节地址不能按连续寻址递增。5.4 时钟同步频繁超时CP56Time2a的IV位被忽略现象主站发时钟同步从站不回确认过一会又发对时请求。抓包看主站下发的时标CP56Time2a第二个字节的bit7IV位是1。原因主站自身时钟未同步或者生成时标的程序把毫秒高位填错了位置导致从站判定时间无效。解决逐字节拆时标确认IV位为0。毫秒值的编码是低字节在前如果毫秒是500字节顺序是F4 01不是01 F4。字节顺序错了从站读出来毫秒值不对IV位也可能被误置。5.5 总召唤响应不完整等不到0x0A结束帧现象主站下发总召唤从站回了一堆遥信遥测数据但主站一直显示“总召唤未完成”。抓包发现数据帧的传送原因都是0x14但没有0x64类型、传送原因0x0A的结束帧。原因部分厂家从站的总召唤响应不按标准三件套回数据发完就结束没有显式的激活终止帧。主站程序如果死等0x0A就会一直挂起。解决调试时先确认主站对总召唤结束的判断条件。规范上必须有激活终止帧但现场设备未必按标准实现。这时候抓包看数据流是否停止如果数据已经发完即使没有0x0A也应该认为总召唤过程完成问题在主站的超时等待逻辑上。这种坑属于规约实现差异查厂家说明书比查抓包更有效。6. 进阶一条命令抓包三步定位链路故障节点现场排查104链路我不喜欢一上来就翻配置而是先抓包看三个节点。抓包命令固定不变tcpdump -i eth0 -s 0 -w /tmp/iec104.pcap tcp port 2404抓完用Wireshark打开按三步走。第一步看U帧握手是否完成。把过滤条件设为tcp.port 2404按时间排序找68 04 07和68 04 0B。只有07没有0B问题在从站或网络设备没把数据回传07和0B都没有问题在TCP层或端口不通。第二步找总召唤闭环。过滤条件用tcp.port 2404 frame contains 64看有没有激活确认07、响应数据14、激活终止0A三件套。只有07没有14说明从站没有上送数据去查从站的数据库映射和公共地址。有数据没有0A多半是厂家实现不标准回主站核对结束帧判断逻辑。第三步看链路保活是否正常。过滤条件用tcp.port 2404 frame contains 43一条TESTFRact必须配一条TESTFRcon68 04 83。如果只有act没有con从站那边t3超时处理有问题链路会周期性断开重连。这三个节点看完80%的链路问题能定位到具体侧。我现场调试的习惯是抓包文件永远保存文件名按“日期_间隔_主站IP_现象”命名比如20240731_线路对点_遥信不上送.pcap。一次对点调试可能要抓几十个包事后复盘翻旧包比翻聊天记录靠谱得多。另外提醒一句tcpdump抓包时-s 0一定要带否则报文被截断长度字段和实际数据对不上白抓。这个参数我吃过亏后来每次写抓包命令都默念一遍。IEC104的报文解析没有太多玄学就是熟读帧结构多拆几个hex坑踩过了就记住了希望帮到你。本文还有配套的精品资源点击获取