新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于IEC104协议的微电网主站客户端:Java解析四遥与高并发落库实战

发布时间:2026/9/29 17:49:00来源:尧图网络
基于IEC104协议的微电网主站客户端:Java解析四遥与高并发落库实战
简介基于IEC104协议的微电网管理系统主站客户端程序面向电力系统自动化与智能电网领域的开发、运维及测试人员用于解决远程监控、数据采集、遥信遥测解析和遥控遥调命令下发等工程需求。程序采用Java编写集成多线程Socket通信与高并发数据库操作适合需要在大批量实时数据场景下保障采集与监控效率的项目落地。压缩包共107个文件大小3.62MB主要包含java源文件23个、class编译文件30个、jar依赖库9个以及用于界面展示的png图片22个和项目配置说明文档xml、properties、txt、md等结构清晰便于二次开发、学习与部署。已有142人学习下载。资料除核心程序与工程文件外还附带了jar依赖包、项目说明文件和辅助资源免去自行搭建依赖与环境的繁琐过程同时json文件覆盖协议配置与数据格式定义便于理解IEC104报文结构及遥信遥测数据的解析流程适合用于电力监控程序的学习、调试与扩展参考。1. 微电网主站客户端一套能解析IEC104四遥、又能落库的Java程序包先说结论真正的微电网数据采集最难的不是算功率而是让主站和几十个RTU按IEC104规约把话说清。这套基于IEC104协议的微电网管理系统主站客户端程序包解决的就是这个问题。它用Java原生Socket做主站侧TCP连接支持遥信遥测解析、遥控遥调命令下发还把多线程通信和高并发数据库操作集成到一起。拆开看里面值得关注的不是业务界面而是“收报→拆帧→解析→批量入库”这条链路的工程写法。适合正被Socket粘包、总召时序、连接池打满折腾的Java开发也适合做微电网、分布式光伏综自系统的联调工程师。2. IEC104四遥解析从字节流到对象地址的完整拆包过程IEC104本质上是TCP承载的IEC60870-5-101应用层主站作为TCP客户端主动连子站的2404端口。真正动手时第一个坑是TCP流没有报文边界第二个坑是ASDU里字段顺序和日常习惯相反。这一章把拆包、链路状态、四遥解析和遥控命令构造的关键点展开顺序刚好也是主站启动后应该做的事。2.1 APDU识别与链路状态机控制IEC104的APDU以启动字符0x68开头第二个字节是APDU长度取值范围是4到253。小于4说明只有控制域一般不会出现。出现大于253或解析位置混乱就要丢掉字节重新找0x68。主站收到TCP字节流后不能直接按固定长度取包必须维护一个累积缓冲区等凑够一整条APDU再交给上层解析。// 接收线程从Socket读到字节后的处理入口 private void onBytesRead(byte[] data, int len) { receiveBuf.write(data, 0, len); while (true) { int avail receiveBuf.availableReadable(); if (avail 2) break; int startPos receiveBuf.currentPosition(); if (receiveBuf.getUByte() ! 0x68) { continue; // 丢掉一个错位字节继续找启动符 } int apduLen receiveBuf.getUByte(); if (apduLen 4 || apduLen 253) { receiveBuf.discardReadedBytes(); continue; } if (avail - 2 apduLen) { receiveBuf.resetToPosition(startPos); break; // 半包等更多字节 } byte[] apdu receiveBuf.readBytes(apduLen); handleApdu(apdu); } }这里用了一个自定义缓冲类核心思路是mark和compact读到一个长度不完整的包时回退到包起始位置把已有数据留到下一轮拼接读到非法长度时直接丢弃整段强制重新同步。不要尝试从错误位置硬解析那样只会让后续好几个包跟着错。拿到整条APDU后先看控制域第一字节判断帧类型。I帧最低位是0S帧最低两位是01U帧最低两位是11。I帧才是带ASDU的应用数据帧S帧和U帧都只有控制域不需要走解析函数。private void handleApdu(byte[] apdu) { int cf1 apdu[0] 0xFF; int frameType; if ((cf1 0x01) 0) { frameType 1; // I帧数据帧 } else if ((cf1 0x03) 1) { frameType 2; // S帧接收确认 } else { frameType 3; // U帧链路控制 } if (frameType 3) { updateLinkState(apdu); return; } if (frameType ! 1) return; byte[] asdu new byte[apdu.length - 4]; System.arraycopy(apdu, 4, asdu, 0, asdu.length); parseAsdu(asdu, 0); }主站启动后的链路状态是STOPPED必须先发U帧STARTDT_ACT等子站回STARTDT_CON后才能进入STARTED状态。很多刚接触IEC104的人会在TCP连接建立后直接发总召子站会视作无效请求日志里什么也不回。U帧里还有一个容易被忽略的TESTFR测试帧作用是保活后面第3章会讲定时发送。2.2 遥信遥测的ASDU解析ASDU头六个字节是固定的类型标识、可变结构限定词、传输原因2字节、公共地址2字节。可变结构限定词的高位是SQ低7位是信息对象个数。SQ等于0时每个对象都带3字节信息体地址SQ等于1时只有第一个对象带地址后续对象地址自动加1。这里最容易翻车的是把地址当成2字节处理IEC104的信息体地址是3字节少读一个字节后面全乱。private void parseAsdu(byte[] asdu, int start) { int typeId asdu[start] 0xFF; int vsq asdu[start 1] 0xFF; int cot (asdu[start 2] 0xFF); int commonAddr (asdu[start 3] 0xFF) | ((asdu[start 4] 0xFF) 8); boolean sqContinous (vsq 0x80) ! 0; int objectCount vsq 0x7F; int pos start 5; int objectAddr 0; for (int i 0; i objectCount; i) { if (sqContinous) { if (i 0) { objectAddr (asdu[pos] 0xFF) | ((asdu[pos 1] 0xFF) 8) | ((asdu[pos 2] 0xFF) 16); pos 3; } else { objectAddr; // 连续地址自动加一 } } else { objectAddr (asdu[pos] 0xFF) | ((asdu[pos 1] 0xFF) 8) | ((asdu[pos 2] 0xFF) 16); pos 3; } switch (typeId) { case 1: { // M_SP_NA_1 单点遥信 int siq asdu[pos] 0xFF; boolean value (siq 0x01) ! 0; boolean invalid (siq 0x80) ! 0; handleYx(objectAddr, value, invalid, cot); pos 1; break; } case 13: { // M_ME_NC_1 短浮点遥测 int raw (asdu[pos] 0xFF) | ((asdu[pos 1] 0xFF) 8) | ((asdu[pos 2] 0xFF) 16) | ((asdu[pos 3] 0xFF) 24); float value Float.intBitsToFloat(raw); int qds asdu[pos 4] 0xFF; handleYc(objectAddr, value, qds, cot); pos 5; break; } default: pos skipTypeSize(typeId, pos, asdu.length); } } }这段代码的小端处理是重点信息体地址低字节在前短浮点也用低字节在前拼int再转float。遥信的品质位bit7是IV无效位bit4是BL闭锁位遇到无效位时不要直接覆盖实时库宁可先标记异常再单独处理。遥测的QDS同理bit7为无效。传输原因cot是1表示周期上送3表示突发20表示总召响应这个字段在后续判断数据来源时很有用。常用类型标识可以先只做这几个实际项目里大多数微电网点位都落在这些类型上。类型标识名称信息元素长度1M_SP_NA_1 单点遥信1字节3M_DP_NA_1 双点遥信1字节13M_ME_NC_1 短浮点遥测5字节45C_SC_NA_1 单点命令1字节50C_SE_NC_1 设定值短浮点6字节100C_IC_NA_1 总召唤0字节2.3 遥控遥调命令构造与确认跟踪命令下发的难点不在拼字节而在“选择-执行”和超时重发。单点遥控用C_SC_NA_1信息元素第0位是SCS0表示分1表示合第3位是S/E1表示选择0表示执行。电力遥控一般要求先发选择命令收到激活确认后再发执行命令。虽然不少设备允许直接执行但规范流程最好保留。// 构造C_SC_NA_1的ASDU部分 public byte[] buildSingleCommandAsdu(int objectAddr, boolean close, boolean select, int commonAddr) { byte[] asdu new byte[10]; asdu[0] 45; // 类型标识 C_SC_NA_1 asdu[1] 1; // VSQ单对象 asdu[2] 6; // COT激活 asdu[3] 0; asdu[4] (byte) (commonAddr 0xFF); asdu[5] (byte) ((commonAddr 8) 0xFF); asdu[6] (byte) (objectAddr 0xFF); asdu[7] (byte) ((objectAddr 8) 0xFF); asdu[8] (byte) ((objectAddr 16) 0xFF); asdu[9] (byte) ((close ? 0x01 : 0x00) | (select ? 0x08 : 0x00)); return asdu; }ASDU部分共10字节控制域由发送模块填充I帧序号放在前四字节。遥控命令不能发了就不管常见做法是用一个并发Map记录待确认命令收到COT等于7的激活确认后再发执行命令。private final ConcurrentHashMapInteger, CmdContext pendingCmd new ConcurrentHashMap(); public void sendCommand(int addr, boolean close, int commonAddr) { byte[] select buildSingleCommandAsdu(addr, close, true, commonAddr); sendIFrame(select); pendingCmd.put(addr, new CmdContext(close, false, System.currentTimeMillis())); } // 收到COT7确认时 CmdContext ctx pendingCmd.remove(addr); if (ctx ! null !ctx.executed) { byte[] exec buildSingleCommandAsdu(addr, ctx.value, false, ctx.commonAddr); sendIFrame(exec); }超时15秒未收到确认要重发或报警。断线重连时这个Map必须清空否则旧确认会串到新的命令上下文里。遥调C_SE_NC_1的结构类似只是信息元素换成4字节浮点加2字节限定词命令流程可以直接复用这一套状态跟踪。3. 多线程Socket与高并发数据库主站客户端不卡死的工程做法IEC104解析本身不复杂真正让主站在高并发下翻车的是Socket读线程和数据库操作互相拖累。这一章把通信线程模型、线程池参数、数据库连接池和批量落库串起来讲顺序就是主站程序从启动到稳定运行的顺序。3.1 通信线程模型单接收线程加任务队列主站侧最忌讳一个点位一条线程。IEC104子站数量一旦超过十几个每来个连接就new一个线程线程切换开销会直接吃掉CPU。常见做法是一个Socket对应一个接收线程读到的字节解析成PointValue后丢进BlockingQueue由业务线程池消费。接收线程只做“读字节和拆包”不碰数据库避免一次慢查询堵住整个Socket。// 通信侧业务线程池初始化 ThreadFactory tf new ThreadFactory() { private final AtomicInteger seq new AtomicInteger(1); Override public Thread newThread(Runnable r) { Thread t new Thread(r, iec104-worker- seq.getAndIncrement()); t.setDaemon(true); return t; } }; ExecutorService bizPool new ThreadPoolExecutor( 4, 8, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue(2000), tf, new ThreadPoolExecutor.CallerRunsPolicy() );核心线程数设为4最大8队列容量2000。这个配置对应微电网主站中等规模几百个遥信遥测点总召一次不超过1万帧。CallerRunsPolicy的意思是队列满后让发送线程自己执行回调代价是发送线程会短暂阻塞实际生产我更建议改成AbortPolicy再单独监控线程池拒绝计数而不是把压力压回通信线程。线程名一定要起好后面查问题看thread dump能一眼认出哪个线程出事。解析后的数据不能直接写MySQL先进队列。队列消费端可以做削峰子站总召瞬间会集中上送大量数据直接写库必然打满连接池。我一般会用一个SizeBlockingQueue消费线程每100毫秒取一次取到500条就批量flush。这个做法后面避坑章还会提到。3.2 数据库连接池与批量落库数据库侧优先用Druid连接池监控、SQL统计和连接泄漏检测都方便。比业务代码更重要的是一组连接池参数很多项目默认配置都太小总召一来就排队。参数建议值说明initialSize5启动时预建连接minIdle5最小空闲连接maxActive20峰值连接上限maxWait5000获取连接超时时间mstimeBetweenEvictionRunsMillis60000空闲连接检测周期removeAbandonedtrue强制回收泄漏连接连接串里一定要加rewriteBatchedStatementstrue。MySQL JDBC驱动默认把addBatch拆成单条执行加了之后才能真正走批量插入。批量写实时表的简化实现如下private void flushToDb(ListPointValue batch) { String sql INSERT INTO realtime_data(addr, value, quality, ts) VALUES(?,?,?,?) ON DUPLICATE KEY UPDATE valueVALUES(value), qualityVALUES(quality), tsVALUES(ts); try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (PointValue p : batch) { ps.setInt(1, p.addr); ps.setFloat(2, p.value); ps.setInt(3, p.quality); ps.setTimestamp(4, p.ts); ps.addBatch(); } ps.executeBatch(); } catch (SQLException e) { log.error(batch insert failed, count batch.size(), e); throw new RetryableException(e); } }实时表用addr做唯一键插入遇到重复就更新保证最新数据覆盖旧值。历史表用自增主键纯追加按时间窗口落库。微电网项目里存在大量断面数据历史表不要写得太细一般5秒一条断面就够了。批量大小我习惯是500条或1秒钟窗口哪个先到按哪个执行。3.3 心跳、看门狗与重连状态机IEC104子站有t1、t2、t3三个超时参数其中t3是子站接收测试帧的最大间隔。主站如果长时间不发TESTFR子站会主动断开连接。解决方法是单独用ScheduledExecutorService每5秒发一次TESTFR_ACT同时记录最近收到报文的时间超过30秒无数据就强制重连。ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleWithFixedDelay(() - { long now System.currentTimeMillis(); if (now - lastReceivedTime 30_000) { log.warn(receive timeout, reconnecting...); reconnect(); return; } if (state LinkState.STARTED) { sendTestFrame(); // TESTFR_ACT } }, 5, 5, TimeUnit.SECONDS);这里有个细节TESTFR不能乱发。如果链路还没恢复到STARTED发测试帧只会占住序号。我在代码里用状态枚举锁住发送动作只有STARTED状态才允许测试帧和总召。重连后第一步重新发起STARTDT第二步重新发总召中间产生的变位报文如果不通过总召补采就会永远丢失。这算是主站客户端最容易被忽视的逻辑。4. 主站客户端避坑记录总召、Socket断线、连接池和品质位的四个坑这一章的问题不是理论推出来的是联调时实际遇到过的事。每一条都按现象、原因、解决写清楚方便读者在日志里对症下药。4.1 总召一直不响应子站日志说“未收到总召条件”现象TCP已经连上发C_IC_NA_1总召子站毫无反应也不回确认日志里什么都没有。原因IEC104链路状态机没有进入STARTED状态或者总召命令里的公共地址和子站配置不匹配。子站的总召条件通常包括链路已启动、当前没有更高级别的传输任务。没有启动链路直接发总召就是“不满足总召条件”。另外公共地址有两种写法IEC104标准是2字节但部分老设备用1字节地址不匹配时子站同样不回。解决连接建立后先发U帧STARTDT_ACT收到STARTDT_CON再发总召。检查commonAddr的字节数跟子站配置文件逐项对比。如果子站要求先对时还要先发时钟同步命令再总召。4.2 偶发“No more data to read from socket”后掉线现象主站运行几小时后日志里冒一条java.io.EOFException: No more data to read from socket然后连接关闭之后如果重连逻辑没写好就一直没数据。原因子站在t3时间内没收到主站的测试帧主动断开或者主站接收线程在解析异常时没有catch住线程跑完退出但Socket对象还挂在连接列表里。很多Java工程在read时只catch了IOException没catch RuntimeException一旦解析器抛空指针接收线程直接死掉。解决接收线程的while循环改成catch IOException和Exception统一走reconnect。定时发送TESTFR_ACT间隔不能超过子站t3参数我习惯5秒一次30秒无收包强制重连。重连要清空状态把链路状态拉回STOPPED再按启动流程走一遍。4.3 数据库连接池打满业务线程全部卡在getConnection现象采集量上来后Druid监控里maxActive打满SQL执行变慢后台报“Connection is not available, request timed out”。原因最常见的真凶是Connection没有释放或者接收线程里同步写库。一个批量insert执行几十毫秒连接池总共20个连接总召一来几百条任务排队几秒就耗尽。还有人在回调里用了同步方法把Socket读线程也拖住形成连锁阻塞。解决所有getConnection都放在try-with-resources里保证finally阶段释放。数据库操作一律从接收链路拆到异步队列接收线程只负责解析和分发。Druid打开removeAbandoned和logAbandoned启动后观察日志里哪些className的调用点泄漏了连接。如果历史表写得太频繁可以先在内存里聚合成5秒一条再写。4.4 遥测偶发大数、遥信闪变问题出在字节序和品质位现象遥测偶尔跳出一个满量程的大数遥信在操作分闸后出现0/1抖动过一会儿才恢复。原因短浮点字节序反了或者把品质位当成了数据位。还有一种情况是I帧序号溢出接收序号比较用了int直接比较大小没有按IEC104的环形序号规则判断。解决解析短浮点时严格按低字节在前拼int再转float。解析SIQ和QDS时先看bit7无效位直接丢弃或标记不要进实时库。序号判断用两个序号差值的绝对值小于32768是否成立或者用取模比较不要用大于小于判断新旧。5. 联调验证与参数调优用模拟子站跑通主站客户端的两个技巧拿到这套主站代码第一步不是接真实站而是先起一个模拟子站把协议交互走通。模拟子站监听2404端口收到STARTDT_ACT后返回确认再收到总召后回一条遥测帧。这样能快速验证主站的拆包、解析、入库和遥控下发全链路。# 简化版IEC104模拟子站只走最基础的交互 import socket srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind((0.0.0.0, 2404)) srv.listen(1) conn, addr srv.accept() # 收到STARTDT_ACT(68 04 07 00 00 00)回STARTDT_CON data conn.recv(1024) print(recv:, data.hex()) conn.sendall(bytes.fromhex(68040B000000)) while True: data conn.recv(1024) if not data: break print(recv:, data.hex()) # 回一帧类型13短浮点遥测地址1值100.5 conn.sendall(bytes.fromhex( 6814 00000000 0D 01 1400 0100 010000 00004842 00))这段代码里的帧长度没有做校验只适合验证主站能收到数据。实际联调建议准备一个能组帧的模拟器把每类ASDU的字节拼好再逐步加入异常场景半包、错位字节、重复总召。验证时有一张检查清单很方便检查项预期结果主站启动后TCP连接2404端口ESTABLISHEDSTARTDT_ACT发出收到STARTDT_CON总召命令模拟器回COT20的遥信/遥测realtime_data表有数据且ts持续更新遥控下发模拟器打印C_SC_NA_1选择和执行一个特别实用的技巧在handleApdu里把收到的APDU以hex形式直接打日志同时打开Wireshark过滤tcp.port 2404两边比对十六进制串。一旦解析结果和抓包对不上很快就能定位是拆包问题还是解析问题不用靠猜。性能验证可以用模拟器批量注入1000个遥测点观察数据库批量写入耗时和队列积压量。如果flushToDb单次耗时超过200毫秒重点检查连接串有没有开rewriteBatchedStatements以及real-time表是否有过多索引。索引只在查询字段上加写入侧越轻越好。我从一次误把总召周期从60秒改成15秒导致子站直接拒绝对话的教训后每次改主站参数前都强制检查一遍对端逻辑版本和t0、t1、t2、t3参数。主站侧和站端参数不一致不是玄学是必然翻车。这套包直接搜“基于IEC104协议的微电网管理系统主站客户端程序”就能找到拿它当主干再按你现场站端参数调一遍。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

奔驰/吉利/比亚迪供应链背后,车灯连接器龙头二闯创业板IPO 2026/9/29 20:32:54

奔驰/吉利/比亚迪供应链背后,车灯连接器龙头二闯创业板IPO

时隔两年,汽车连接器制造商思索技术再次叩响资本市场大门。早在2023年12月,东莞市思索技术股份有限公司(以下简称“思索技术”)首次冲击创业板IPO,但从申请获受理到撤回仅用了29天。如今卷土重来,拟在创业板…

阅读更多 →
OpenRouter Deepseek 使用MCP服务的问题:TaoToken 统一 Key 下的 config.toml 骨架与报错排查 2026/9/29 20:32:54

OpenRouter Deepseek 使用MCP服务的问题:TaoToken 统一 Key 下的 config.toml 骨架与报错排查

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

阅读更多 →
多格式测试工件差异流水线:JSON、YAML、CSV、日志与文档的统一比较 2026/9/29 20:32:54

多格式测试工件差异流水线:JSON、YAML、CSV、日志与文档的统一比较

多格式测试工件差异流水线:JSON、YAML、CSV、日志与文档的统一比较摘要:测试结果分散在 JSON、YAML、CSV、日志、Excel 和 Word 中,直接文本 diff 会被字段顺序、时间戳、浮点格式和文档元数据淹没。本文设计“识别—解析—规范化—语义比较—…

阅读更多 →
【十月会议优选:巢湖学院主办 | IEEE出版 | 三亚会议】第九届机械、控制与计算机工程国际学术会议(ICMCCE 2026) 2026/9/29 20:32:54

【十月会议优选:巢湖学院主办 | IEEE出版 | 三亚会议】第九届机械、控制与计算机工程国际学术会议(ICMCCE 2026)

第九届机械、控制与计算机工程国际学术会议(ICMCCE 2026) 2026 9th International Conference on Mechanical, Control and Computer Engineering 2026年10月16-18日 | 中国三亚 大会官网:www.icmcce.com 截稿时间:见官网&…

阅读更多 →
雅思核心词汇背诵,告别死记硬背,高效掌握秘诀! 2026/9/29 20:32:54

雅思核心词汇背诵,告别死记硬背,高效掌握秘诀!

雅思考试对英语水平要求较高,词汇量是衡量英语水平的重要指标之一。如何在短时间内高效地掌握雅思核心词汇呢?今天就来跟大家分享一些实用的单词记忆方法、学习习惯和家庭教育心得。 一、单词记忆方法 1. 结合词根词缀记忆法:将单词分解为词根…

阅读更多 →
风格分类和时装报道怎么一起看 2026/9/29 20:32:48

风格分类和时装报道怎么一起看

风格分类和时装报道怎么一起看 风格分类是一套稳定的名字,时装报道是这一季发生的事。穿搭风格一般分哪几类,看尚报(https://chicbrief.com/style)的 12 种档案;这一季谁在用这些语言,看尚报的资讯&#xf…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉