
IEC104协议下的文件传输与软件升级从报文机制到现场交付的完整实操手册干电力二次系统、调度自动化或者变电站运维的朋友对IEC 60870-5-104肯定不陌生。不过大部分人对它的印象基本停留在四遥——遥测、遥信、遥控、遥调最多再加个电度。但真正到了现场改造、固件迭代、参数整定或者规约转换器升级的时候你才会发现IEC104其实还隐藏着一套完整的文件传输机制可以用来传定值单、传录波文件、传日志甚至直接承载整个装置的升级包。我最早接触这块是好几年前做一个变电站网关机的远程升级项目当时厂家给的资料又少又散规约文本翻烂了才把一套流程跑通。这篇就把我实际工程里验证过的细节和流程一次说清楚给正要上手的同行省点弯路。有人可能会问文件传输和软件升级不是两件事吗为什么要放一起讲原因在于IEC104并没有专门为软件升级设计一套独立的协议体系工程上普遍的做法就是用它的文件传输功能作为底座把固件包、升级包、校验文件当作普通文件传过去再配合从站端的Bootloader或应用层逻辑去完成切换和激活。所以这篇文章的主线就是先把IEC104文件传输的机制彻底弄清楚再讲怎么用它安全、可靠地把一个升级包送到几十上百台装置里去。文章里我不会只给报文格式表格就完事而是会深入到为什么这条报文要这么填为什么这个环节容易出问题卡住的时候先查哪里把协议文本背后那层工程经验摊开来讲。适合正在写主站程序、做规约调试、搞设备运维改造的工程师参考也适合刚接触电力规约、想系统理解IEC104报文怎么承载业务数据的同学。1. 为什么IEC104能承担文件传输任务协议基础与适用边界1.1 从IEC 60870-5-104的定位说起远动规约里藏着的通用传输通道IEC 60870-5-104规约本质上是IEC 60870-5-101规约在TCP/IP网络上的映射。它的底层传输是TCP端口号固定为2404这意味着它天然拥有面向连接的、可靠的字节流通道。可靠传输这件事在文件传输场景里太重要了因为文件数据不允许丢不允许错哪怕是多一位少一位整个升级包就废了。TCP的确认重传机制恰好解决了这个问题这也是规约设计者选择在104上实现文件服务的底气。但在实际使用中要特别注意虽然TCP本身可靠可IEC104为了保证实时性还定义了自己的应用层超时参数比如t0连接建立超时默认30秒、t1发送或测试帧确认超时默认15秒、t2接收确认超时默认10秒以及k最大未确认APDU数量默认12个和w最大未确认APDU后触发确认的阈值默认8个。这些参数在传普通遥测遥信时影响不大但传大文件时如果从站接收侧处理慢超过t1还回不了确认帧主站就会强行断开链路。我见过好几个项目把文件传输失败的问题查到最后根子都是t1设置太短从站在边写Flash边回确认兜不住。所以首先要建立的一个认知是IEC104文件传输的可靠性不是白来的它是TCP可靠传输应用层超时管理文件事务状态机三层叠加的结果。后面每一层都可能成为故障点。1.2 文件服务适用的三类场景定值、日志与软件升级理论归理论我按现场实际碰到过的需求把IEC104文件传输的适用场景归成三类方便你对照自己的项目来判断方案是否可行第一类是参数定值类文件传输。比如保护装置的定值单、故障录波器的配置文件、测控装置的遥测系数表。这类文件的特点是体积小几KB到几十KB、变更频繁、对完整性要求极高。通过104的文件服务主站调度端可以把最新的定值单直接下发到各个间隔层装置省去人工到现场用笔记本连接装置改参数的麻烦。这类应用在国内许多新一代智能变电站里已经是标配功能。第二类是日志与录波数据回传。故障录波文件、SOE事件记录、装置自检日志这些文件往往是在故障发生后急需拿到的。传统做法是运维人员到现场用U盘拷贝或者通过独立的录波联网系统。而使用104文件服务后调度主站可以直接远程把录波文件从装置里拉回来大大缩短了故障分析周期。拉文件用的是什么报文是用召唤文件目录然后逐条选择文件再发起取文件过程的请求序列这个在后面章节会详细拆。第三类就是软件升级。把新版本的固件包通过104通道传到目标装置再激活升级。这也是本文的核心场景。它比前两类更复杂因为升级包往往几百KB到几MB已经属于大文件范畴而且升级动作本身具有不可逆性——传错了、传一半断了、校验不过就强行升级装置就变砖了。所以软件升级场景下工程上不仅要用到104文件传输的全部核心机制还要在业务层额外增加版本确认、校验码核对、升级窗口管理等机制。1.3 不是所有从站都实现了文件服务设备兼容性要先摸底这是我最想提醒的一点。IEC60870-5-104规约本身很长文件传输只是其中可选的ASDU集合之一并没有强制要求每个从站设备必须实现。国网、南网的标准化规范里虽然对变电站监控系统与间隔层设备的104通信做了详细规定但在文件服务这一块不同厂家的实现程度差异很大。有些老型号的保护装置整个固件里压根没有文件服务的代码你用总召唤看不到文件目录条目发送召唤文件目录报文后要么不回要么返回未实现。这种情况下你强行做远程升级只能依赖厂家的私有规约或者IEC 61850的日志/文件传输能力了不能死磕104这条路。还有一类设备更麻烦它实现了文件服务但目录结构、文件命名、传输块大小、支持的文件类型标识都是厂家私有的。比如A厂的文件目录路径可能是/FLASH/USER/UPDATE.binB厂却是\app\update\v2.1.0.hex而且两者都不带通配符匹配。这意味着主站程序里的文件路径/名称必须做成可配置项不能写死。我在项目里就吃过这个亏前期用某国产装置调试得很顺换了一个合资品牌的装置后目录解析全乱排查了整整三天才发现是路径风格不兼容。所以第一步永远是摸底先把手头需要接入的设备清单列出来逐个确认支持哪些ASDU类型编号、文件服务支持到什么程度、目录格式长什么样。这个信息通常在设备的规约一致性测试报告或通信规约说明书里能找到。拿到之后再做主站侧的适配层设计。2. 深入报文内部文件传输相关的ASDU类型与状态机机制2.1 文件服务核心ASDU类型逐一拆解IEC104的文件传输机制并不是用单独的APDU类型实现的而是在ASDU层面定义了一系列与文件相关的功能类型。这里先把最关键的几种类型标识码列出来这是你抓包分析的基础背不下来没关系但一定要形成肌肉记忆类型标识简称名称方向核心作用120F_SC_NA文件准备就绪从站→主站从站告诉主站我准备好了可以开始传了121F_SR_NA文件段从站→主站实际文件数据一段一段传122F_SG_NA文件节主站→从站主站向从站请求指定文件段123F_LS_NA文件结束从站→主站最后一个文件段或确认文件传输完成124F_AF_NA文件确认双向确认收到某个文件段或文件125F_SC_NB文件准备就绪带子文件从站→主站传输对象包含子文件时使用126F_SR_NB文件段带子文件从站→主站带子文件信息的文件段127F_SG_NB文件节带子文件主站→从站带子文件信息请求指定段128F_LS_NB文件结束带子文件从站→主站带子文件信息的传输结束129F_AF_NB文件确认带子文件双向带子文件信息的确认看到这里你可能有点晕带A和带B后缀有什么区别简单理解带A的是基础版本适用于单文件或整个传输集的传输带B的引入了子文件概念适用于一个大文件被划分成多个子文件后分组传输的场景。在实际的软件升级场景里一个固件包超过从站接收缓冲区上限时厂家可能就把这个升级包拆成多个子文件比如每个256KB用带B的类型逐个子文件传全部传完再从站侧合并。这个细节直接决定了你主站程序怎么写一定要提前确认设备用的是A类还是B类。另外文件传输还涉及两个不那么显眼但极易踩坑的控制方向类型F_SC_NA120里带有一个传送原因字段和控制方向的选择/激活/结束/放弃/状态查询等限定词文件确认F_AF_NA124里既有肯定确认ACK也有否定确认NAK。比如你请求第3个文件段从站却回了NAK说明它内部存储状态跟主站认知不一致。这时必须回到文件选择阶段重新走流程不能继续发第4段否则必乱。2.2 谁发起、谁应答主站/从站在文件传输里的固定角色IEC104文件传输的角色非常固定主站是控制方主动发起方从站是受控方响应方。但在不同的业务子流程里谁发文件并不是一成不变的。这里有一个容易混淆的概念必须提前理清。文件传输分为两大类操作取文件读取和存文件写入。在取文件场景里从站是文件提供者文件流方向是从站→主站比如调度端拉取故障录波文件在存文件场景里主站是文件提供者文件流方向是主站→从站比如调度端下发固件升级包。虽然数据流方向不同但状态机的控制方式都是由主站发起的比如选择文件请求文件段激活传输结束传输这些控制ASDU一定是主站发的。从站只是被动地响应要么回文件段数据要么回确认要么回否定。为了让你有直观印象我描述一下取文件的过程也就是主站从从站拉取一个日志文件主站发送召唤文件目录带目录名称从站逐一返回文件名、长度、时间标签等目录信息。主站发送选择文件ASDU指定要取哪个文件。从站校验通过后返回文件准备就绪ASDU里面带着一个文件标识和总长度。主站计算好总共要几段根据从站支持的单段长度然后按顺序发送请求文件段ASDUF_SG_NA逐段把文件抠出来。从站每收到一个段请求就返回对应的文件段数据F_SR_NA。主站收完最后一段后发送文件结束并确认或直接发送请求文件段携带最后一个段序号从站确认后整个取文件过程完成。这个过程里最核心的状态是当前文件事务ID主站和从站都必须在内存里维护一个当前文件ID。一旦链路中断或者报文丢失重连后这个ID如果没有重新协商两侧状态就会错位直接表现为我请求了第5段你回了第8段。所以要实现健壮的文件传输光按报文格式发是不够的还必须实现事务级的超时和回滚机制。2.3 文件段定长与序号看似简单却最容易错位的设计文件传输最底层的交互单元是文件段Segment。IEC104标准里并没有强制规定文件段的固定长度而是把它作为参数在上层协商或者由从站通过文件准备就绪报文里的信息体来声明。不同厂家的设备差异能大到让你崩溃有的每段128字节有的256字节有的干脆每段就是一个完整的小文件甚至几百KB一次传完。主站侧的通用做法是第一次选择文件后从从站返回的文件准备就绪里解析出单段长度字段然后严格按照这个长度去切片请求。如果主站忽略了这个字段按自己默认的512字节去请求而从站实际实现的是256字节一段那么从站要么拒绝请求要么按自己的方式对齐返回最终主站拼接出来的文件就是错位的乱码。这个问题的排查很隐蔽因为报文交互流程看起来每一步都对文件总长度也对得上但文件内容就是不对。只有在抓包后对比每一段的序号偏移量才会发现段长不匹配。文件段序号还有一个坑是起始序号。在IEC104的文件传输ASDU里文件段是带有一个文件段序号字段的多数实现从0或1开始但没有全局统一标准。我建议你在主站程序里不要把起始序号写死而是解析文件准备就绪ASDU里给出的第一个段序号并把它作为本地计数基准。同时每收到一个文件段都要校验当前段序号上次段序号1防止漏段或者重复收段。重复收段可以直接丢弃漏段则必须重新发起从漏掉的段开始的请求而不是继续往后走。3. 文件目录的召唤与解析升级包能被找到的前提3.1 召唤目录的报文怎么填类型、传送原因和目录标识做软件升级主站第一步往往不是直接发文件而是先看一眼从站上有没有升级包存放目录或者看看从站当前固件版本、升级包版本信息。这个能力靠的是召唤文件目录的ASDU。以最常用的F_SC_NA120类型为基础召唤目录时主站发送的是一个控制方向的信息体信息体地址指向特定的目录标识。在工程中目录标识通常直接复用文件标识的命名空间比如用0x00000000表示根目录用0x00000001表示升级包存放目录。但这同样缺乏统一标准必须和设备说明书对齐。报文里传送原因COT的取值是一个关键点。常规的周期召唤、总召唤是20号传送原因但文件目录召唤一般用的是请求类原因比如5请求或6激活具体看规约文本里对文件服务的定义。我建议直接参考IEC 60870-5-5和IEC 60870-5-104应用层中对文件服务传送原因的约束不要想当然地照抄遥信总召唤的传送原因。3.2 从站目录条目的典型返回格式文件名、长度、修改时间从站收到目录召唤后返回的不仅是一个文件名列表而是一组结构化的目录条目。每个目录条目通常包含以下关键信息文件标识4字节主站后续选择文件时要原样带回这个标识它是文件在从站内部存储的唯一句柄。文件名人可读的字符串比如fw_v2.1.0.bin但必须注意它的编码格式多数厂家用ASCII少数用GBK或UTF-8解析时不要默认。文件长度以字节为单位4字节无符号整数最大能表示4GB对固件包完全够用。文件修改时间可能是CP56Time2a格式7字节也可能是简化格式看厂家实现。文件属性只读、隐藏、系统文件、临时文件等升级包一般要求是普通可读文件若是只读文件写入流程会被拒绝。解析目录时我建议不要做过多假设而是用一个通用的结构体去接收原始字节然后在业务层做字段映射。因为目录条目的字段顺序和填充方式在规约文本里给了基础定义但不同厂家在文件名长度这个字段的取值上经常不一致有的包含结尾\0有的不包含有的用固定32字节长度补齐有的用变长字段。抓包看清真实数据再写解析代码能省去后面联调时的大量返工。3.3 目录信息在升级流程中的实际应用版本比对与空间校验在实际的软件升级流程里召唤目录并不是为了看看而是要做两件重要的事。第一件是版本比对。主站需要把从站的当前版本号通常也放在目录里的某个配置文件中或通过其他遥信上送与待升级的目标版本号做比较只有确认目标版本号高于当前版本号才允许升级。我见过有工程省掉这一步结果现场把装置从高版本刷成了低版本触发了一堆安全告警。版本比对信息最稳妥的获取途径就是读目录里的版本配置文件而不是靠遥信——遥信可能因为设置错误误导你。第二件是空间校验。从站虽然不像PC那样动辄几百GB存储但它存放升级包的Flash区域是有上限的。主站教条地去传一个比从站剩余空间还大的升级包从站端会写入到一半报存储空间不足。这个错误往往在传输进行到60%、甚至90%的时候才暴露非常恶心。所以传之前读目录时就要额外解析目录里是否包含剩余空间这类特殊条目然后拿升级包大小去比对空间不够就提前中止流程并给出明确告警。4. 升级包管理规范与单包/分包传输的选择逻辑4.1 固件包为什么需要预处理校验文件与版本清单直接把编译出来的裸固件app.bin丢给104通道传输是一种不负责任的做法。工业级远程升级必须对固件包做预处理形成一套升级包束。我的习惯是生成三个文件固件本体文件fw_v2.1.0.bin包含完整的应用程序或Bootloader应用程序合并镜像。校验文件fw_v2.1.0.md5里面存放固件本体的MD5散列值或者SHA256看从站支持能力。版本清单文件version.json或version.ini里面包含版本号、硬件平台、发布时间、依赖的最低Bootloader版本等信息。这三个文件通过104文件服务依次传输到从站后从站端先校验MD5与固件一致性再由Bootloader检查版本清单里的硬件平台是否与自身匹配。只有这些都通过了才允许切换到升级流程。这一步预处理看似增加了复杂度但能挡住绝大多数刷错固件导致装置起不来的惨案。我在一个光伏电站项目里就靠这套机制拦住了一次混用固件的批量升级事后想想都后怕。4.2 一次传一个文件还是分包传IEC104没有MultiFile但有折中方案我能不能把三个文件合并成一个tar包直接传这个问题几乎每次评审都会被问到。技术上当然可以IEC104不会限制你传的是什么内容。但工程上我不建议这么做原因有三可观测性差合并包传完后从站还要解包、拆包、逐个校验哪个环节出错都不好定位。分开传主站可以从目录里逐个确认每个文件是否到位。断点续传复杂合并包一旦传了一半断了重传时是整个包重传还是从断点续传如果做到段级断点续传那从站端必须维护一个巨大的位图来记录哪些段已经落盘逻辑复杂度飙升。从站接收缓冲约束很多从站设备的文件接收接口是按单文件设计的你直接扔一个合并包可能因为文件体积超过其单文件上限而被拒绝。比较推荐的折中方案是三个文件分别传输但把版本清单放在最后传。因为从站可以约定版本清单文件到达即触发整体升级包的可用性校验。这样前面两个文件传得磕磕绊绊也没关系只要最后一个文件到位就代表这次升级准备动作完成了。这也给了主站一个明确的业务级信号所有文件都传完了可以进入激活阶段了。4.3 实际升级包按段切分的计算方法一个684KB固件的拆包示例下面我给你一个可复现的计算示例加深对文件段这个概念的理解。假设待传的固件fw_v2.1.0.bin大小为684KB从站设备在文件准备就绪报文中声明它的文件段长度为256字节。计算过程如下先算总字节数684 * 1024 700416字节。第一到倒数第二段都取满256字节只有最后一段可能不足。需要的文件段总数ceil(700416 / 256) ceil(2736) 2736段。前2735段每段256字节共2735 * 256 700160字节最后一段为700416 - 700160 256字节。所以恰好整除没有短段。但如果固件大小是700000字节则前2734段满256字节合计699904字节第2735段就只有96字节。这样逐段请求的序号范围就是0~2735如果起始序号为0。发请求时就可以用一个循环#define SEGMENT_REQUEST_SIZE 256 #define FIRMWARE_SIZE 700416 int total_segments (FIRMWARE_SIZE SEGMENT_REQUEST_SIZE - 1) / SEGMENT_REQUEST_SIZE; for (int seg_idx 0; seg_idx total_segments; seg_idx) { // 组装文件节ASDUF_SG_NA信息体里携带当前段序号 // 发送给从站等待对应的文件段ASDUF_SR_NA返回 send_segment_request(seg_idx); // 实际工程中这里要做超时重传不是简单地同步等待 }有一点必须强调虽然上面写的是同步循环但在真实主站里程序最好不要发一个段请求死等一个段响应而是用窗口机制在允许的未确认文件段数量范围内连续发送多个段请求再批量接收文件段响应以此充分利用104链路的TCP带宽。窗口大小可以参考规约里的k参数但一般建议取1~4个段不要一上来开满12个因为从站写Flash或者文件系统的速度往往跟不上。4.4 从站侧Bootloader与升级包的协作逻辑谈升级就绕不开Bootloader。在IEC104文件传输把升级包完整写入从站的应用暂存区后真正执行固件替换的是从站内部的Bootloader程序。Bootloader和应用固件的协作逻辑在工程上通常是这样设计的应用运行模式下通过104服务把升级包写入APP_UPDATE分区或指定文件系统的升级目录。应用把校验结果MD5匹配、版本号匹配写入一个固定的标志位区域比如UPDATE_FLAG然后软复位或定时重启。Bootloader在启动时检查UPDATE_FLAG若有效则从暂存区读取新固件覆盖写入应用区写入完成后修正标志位再跳转执行新应用。这里常见的坑有两个。第一个是应用区大小限制有些老装置的Bootloader只支持不超过512KB的应用你的新固件一超过512KBBootloader写入时会报区域溢出。这个问题用104文件传输是绕不过去的只有升级Bootloader或者修改分区表才能解决。第二个是升级掉电恢复Bootloader在写应用区的过程中如果突然掉电可能导致应用区损坏。优秀的Bootloader会采用双区备份标志位切换方案保证总能回退到旧版本。选型时优先选带双备份能力的设备如果设备不支持那远程升级就必须搭配现场值守人员进行断电事故应急。5. 端到端的软件升级操作流程从总召唤到激活重启的完整时序5.1 第一步链路建立与应用层总召唤升级的第一步不是直接传文件而是先建立一个可靠的IEC104应用链路。主站向从站发起TCP连接端口2404然后交换STARTDT激活帧U帧确认应用层数据传输已激活。紧接着进行总召唤C_IC_NA类型标识100让从站上送当前的遥测、遥信、以及文件服务相关的状态信息。不要小看总召唤这一步。有些从站只有在收到总召唤后才会初始化文件服务上下文而且总召唤过程中文件服务并不一定可用——你必须在总召唤链路空闲后再发起文件操作。我建议在逻辑上做一个忙等机制收到总召唤的结束标志激活终止COT10之后再开始文件服务流程。如果总召唤还没结束就发文件请求很多从站会直接忽略或者产生应用层竞争丢包后处于莫名其妙的状态。此外总召唤也是确认从站在线、版本号与配置匹配的一次好机会。可以在总召唤响应里顺便解析从站的软件版本号信息体如果厂家把它映射成遥信或遥测的话。这样升级前的版本校验就可以提前完成不用等到文件目录召唤阶段。5.2 第二步逐文件传输的完整包序号流程我把一次传一个升级文件的完整时序写出来供你对照排查。假设主站要向从站写一个升级包文件fw_v2.1.0.bin主站发送文件选择ASDU类型标识124F_AF_NA控制方向在信息体地址里携带目标文件名/路径或文件标识。从站回应文件准备就绪ASDU类型标识120F_SC_NA监视方向里面包含文件标识、总长度、段长等参数。这一步如果回了否定确认说明目标文件已存在且被占用或者路径不存在。主站依次发送文件节请求ASDU类型标识122F_SG_NA每一条携带段序号。从站每收到一条就定位文件偏移读取对应段返回文件段ASDU类型标识121F_SR_NA。主站发送文件结束并确认ASDU等所有文件段都收到后主站发送带结束并确认限定的文件确认ASDU类型标识124从站返回确认后单个文件写入过程完成。这里要特别提醒每个文件传输之间必须等到从站返回文件确认才能开始下一个文件。不能像流水线一样第一个文件还没确认完就发第二个文件的选择否则从站端的文件事务上下文会冲突。在我见过的诸多主站实现里把文件传输状态机做成严格串行是最稳的。性能上的损失可以忽略因为升级场景本来就不是高频业务。5.3 第三步激活升级命令与重启时序的把握当版本清单、固件本体、校验文件三者都成功写入从站后主站发送专门的激活升级命令。这里有一个设计细节激活升级命令在IEC104标准里没有专门的文件类型标识所以工程上通常复用遥控命令ASDUC_SC_NA/C_DC_NA类型标识45/46把某个升级触点映射成升级激活位置或者使用文件确认ASDU里特定的限定词结合文件名来触发。无论采用哪种方式核心原则是激活命令必须是最后一步且必须和文件传输过程彻底分开。因为文件传输可能会因网络原因反复重试但激活命令一旦发出从站就可能立刻进入Bootloader切换流程此时链路会中断几秒到几十秒。如果主站和文件传输程序没有做好激活后链路断开是正常现象的处理就会把这个断开误判成故障触发重连风暴干扰从站升级。从站收到激活命令后的典型动作是对升级文件做最终CRC或MD5校验校验通过则写入升级标志位返回遥控确认或文件确认延时一段通常1~3秒给主站留出收到确认的时间后自动重启进入Bootloader流程。主站侧在发出激活命令后应该进入升级观察期周期发起TCP重连或STARTDT激活等待从站在新版本下重新上线并上送总召唤。如果超过设定时间比如5分钟仍未上线才判定升级失败并告警。这个升级观察期窗口一定要大于从站升级、重启、初始化文件系统的总耗时否则容易把正在升级的正常设备误判为故障。5.4 升级结果确认从站重新上线后的必查项从站重新上线后第一件事不是高兴地宣布升级成功而是要做几项确认核对软件版本号从站上送的自检/版本遥信或者通过文件目录读取版本文件确认当前运行版本等于目标版本。检查文件系统完整性重新召唤一次文件目录确认升级包暂存区里的文件与预期一致没有残留或损坏。检查运行状态从站的遥信里有没有新增的告警比如文件系统错误应用校验失败Bootloader回退这些告警往往比版本号更能说明升级是否真成功。只有在以上三项都通过的情况下才能把这次升级标记为成功。如果版本号是新的但有文件系统错误告警就要警惕是升级包不完整导致的边缘故障建议尽快安排现场检查。6. 文件跨网穿透与链路抖动场景下的异常处理6.1 调度网与变电站安全分区之间的文件传输边界问题在实际的电力监控系统里调度主站和变电站设备之间往往跨着安全分区甚至中间有反向隔离装置或正反向隔离阵列。IEC104的TCP流并不能直接穿过隔离装置因为隔离装置通常在物理层和应用层做了单向控制。我在处理跨网文件传输时遇到过的典型拓扑有两种需要区分对待第一种主站和从站位于同一安全分区内比如站控层A区到间隔层A区。此时IEC104可以直接通信文件传输就按正常流程做只要保证链路参数匹配就行。第二种主站位于III区或IV区从站位于I区或II区。这时104链路根本无法直连必须在安全边界部署文件摆渡服务。具体做法是在安全I/II区一侧部署一个采集网关网关通过IEC104文件服务从装置取文件或下发文件然后通过反向隔离装置把文件摆渡到III/IV区。主站侧看到的就是一个前置文件服务的代理它屏蔽了底层的网络边界复杂性。这种场景下IEC104文件传输本身的技术细节不变但升级包的传输路径从主站直连从站变成了主站→摆渡网关→隔离装置→采集网关→从站。每一跳都可能出现文件校验失败或目录解析不一致的问题。我的建议是在摆渡网关上也做一次文件MD5校验不要指望端到端原样透传。有一回我们就是因为隔离装置对文件内容做了格式扫描把内含特定关键字的文件拦下修改了元数据导致最终端到端校验失败排查了很久才发现是中间设备动了文件。6.2 链路断开后的文件会话恢复为什么断点续传不能乱用IEC104文件传输并不像FTP那样自带真正意义上的断点续传语义。它虽然有文件段序号但当链路断开后主站和从站的文件事务上下文都会重置。如果从站重新连接后主站还接着断点时的段序号继续请求从站轻则返回否定确认重则因为文件句柄失效导致整个文件传输接口挂起。那现场怎么处理大文件中断我采用的是专项行动式重传策略链路断开后主站先重新建立TCP连接发起STARTDT激活。重新进行总召唤或至少重新召唤一次文件目录。根据目录里该文件的实际长度与本地已接收长度对比如果本地已接收长度为0重新走一遍选择文件→文件准备就绪→逐段传输的完整流程。如果本地已接收长度大于0但小于文件总长度此时不要盲目续传而是先检查从站是否支持指定起始偏移的文件节请求。极少有从站支持多数情况下还是要从头传。如果文件体积太大重传成本高那就考虑在从站侧删除半成品文件再重新下发一次。这里要提一个工程技巧虽然IEC104本身不提供断点续传但你可以在业务层自己实现文件级断点续传——把升级包在预处理器上就切分成多个物理小文件比如每个256KB104层面分别传输每个小文件。某个小文件传坏了主站只需要重传那一个小文件而不是整个大包。这就是第4章A/B类ASDU里带子文件类型存在的实际意义。代价是主站程序要为每个子文件维护独立的传输状态和校验状态代码复杂度高一些但对于超过1MB的大固件包这个代价完全值得。6.3 文件段超时重传与对时/总召唤的优先级管理在文件传输过程中尤其是大文件不可避免地会出现某个文件段响应超时。处理超时我总结了三条优先级原则不要一超时就把整条104链路断开重连。先尝试应用层重发文件节请求重试2~3次如果仍然超时再看从站是否还在正常运行发一个U帧测试帧TESTFR激活。重发文件节请求时保持其他文件段的接收缓存清空。因为网络是乱序的TCP层虽然有序但应用层接收到文件段后如果发现不是当前期望的段号绝不能缓存下来硬拼而应丢弃并重新请求期望段。升级期间暂停周期召唤和对时任务。我见过有些主站一边传文件一遍做周期总召唤从站的通信CPU被周期任务占满文件段处理不及时触发超时形成恶性循环。在升级文件的整个传输窗口内把周期召唤、对时命令全部挂起只保留测试帧保活能显著提升文件传输成功率。从站侧同样要注意如果从站实现的文件服务是单线程的那它在写Flash时可能无法响应其他应用层请求。主站若在文件传输期间发控制命令从站有可能会先存到队列等文件传输完成再执行导致操作延迟。这一点在项目验收时最好作为一项测试用例明确记录。7. 从报文抓包到现场故障排查完整排障链路示例7.1 抓包工具的选择与过滤条件现场排查IEC104文件传输问题抓包是必须的手段。我常用的组合是Wireshark加上一个支持IEC104解析的插件版本因为新版Wireshark自带的IEC 60870-5-104dissector已经能解析大部分ASDU类型包括120~129这些文件服务类型但偶尔也有解析不全的情况这时就要配合十六进制原始报文来看。推荐设置过滤条件tcp.port 2404如果主站和从站之间有NAT或者负载均衡可能需要过滤IP端口ip.addr 192.168.1.10 tcp.port 2404然后在Wireshark的Decode As里确保2404端口被强制解码为IEC 60870-5-104。抓包的重点是确认三个阶段链路建立与STARTDT激活、文件选择与准备就绪、每个文件段的请求与响应。我一般会先在Wireshark里按ASDU类型做统计看120/121/122/124这些类型是否都有出现如果缺了某个类型说明是从站不支持还是主站根本没有发。7.2 一个文件段请求被否定案例的完整排查过程讲一个真实的排障过程。有一个项目里主站向从站传升级包走到第1780个文件段时从站突然回了否定确认NAK主站立即中止了传输。现场排查链路如下第一步先看了抓包发现主站发的是128号文件节请求F_SG_NB带子文件但从站回的是129号文件确认F_AF_NB确认里带了否定标志。说明从站的应用层是明确知道这个请求有问题的不是网络丢包。第二步对比第1779段和第1780段的请求第1779段请求的子文件索引是0第1780段变成了1。而查看从站手册发现当子文件索引变化时从站要求主站必须先发送一个新的文件选择流程重新初始化子文件的句柄才能继续后续段。主站程序只看了文件级分段没有处理子文件切换逻辑违反了从站实现预期。第三步修改主站逻辑在子文件索引变化前先走一遍文件选择流程成功后再请求第1780段。整个流程恢复畅通。这个小案例说明了什么它说明IEC104文件传输在带子文件的B类类型下文件会话状态比普通文件段序号复杂得多。除非你有十足把握否则我建议优先使用A类类型不带子文件进行传输把文件切片的上层逻辑放在业务层自行处理减少对从站私有实现的依赖。7.3 常见异常码对照与快速定位表现场排障时你一定会遇到各种否定确认或异常状态码我整理一个常用对照表帮助你快速定位问题方向异常现象可能原因排查方向目录召唤无响应从站不支持文件服务确认从站规约版本与一致性测试报告选择文件返回NAK文件路径错误或文件被占用手工确认从站端文件系统实际路径文件准备就绪里段长为0从站未正确解析选择命令检查信息体地址映射与文件名编码请求第N段返回NAK文件句柄过期或子文件索引变化重新执行文件选择流程后重试传输到一半链路断开t1超时或从站在写Flash时假死增大t1、降低传输速度、暂停周期召唤文件传输完成但校验失败主站段长与从站段长不一致对比文件准备就绪里的段长字段并强制执行升级后装置无法启动固件与硬件平台不匹配检查版本清单中的硬件平台字段这张表不能替代手册但能大幅缩短你看协议的排查时间。遇到问题先对照这张表定位方向再翻具体设备的规约说明书效率高得多。8. 关于实现方案选型与工程交付的几条个人建议最后这几条属于我在项目收尾阶段才会认真想的事但越早定下来后面越省心。建议你在正式动工写代码前就考虑清楚第一主站文件传输模块最好做成独立于四遥处理模块的子进程或独立线程。IEC104文件传输和实时数据采集是两种完全不同节奏的任务文件传输可能长时间占用链路资源但它又不允许频繁被打断。把它们耦合在一个线程里要么实时数据延迟大要么文件传输超时多。我见过有主站程序把文件传输放在总召唤子程序里做导致总召唤一次要跑好几分钟后来还是拆开了。第二升级业务必须做到可回退。除了从站固件本身的双备份机制主站侧也要保留上一版本升级包的归档和下发能力。一旦新版本在运行中发现严重问题要能通过同一条104链路把旧版本刷回去。项目交付时这个回退演练应当和升级演练一样纳入验收测试不能只做正向流程。第三不同厂商的104文件服务实现参差不齐在项目启动初期就应当做一份设备能力矩阵表把每台装置支持的文件类型、段长、目录风格、是否支持子文件、是否支持激活升级命令等逐项登记。这个矩阵表既是开发人员的开发依据也是现场实施人员的操作手册。靠脑子记这些差异迟早要出事。我在实际项目中还养成了一个习惯每次升级操作完成后把主站侧记录的段请求时序、从站侧确认信息、最终校验结果全部导出成一份电子档案归入设备台账。这样下次升级或故障回溯时有据可查。用IEC104做软件升级本质上不是一件难事难的是把每个环节的细节都管理起来。把报文机制吃透把状态机管好把兼容性摸清这套流程就能稳定地服务你的整个站端设备群。