3、SECS/GEM消息结构:Message Header与Data Item的组成,以及Block与Multi-Block传输机制
SECS/GEM协议的核心在于其结构化的消息格式。理解消息头(Message Header)与数据项(Data Item)的组成,以及底层传输时的分块(Block)与多块(Multi-Block)机制,是进行高效调试与抓包分析的基础。本章将深入剖析这些关键概念,帮助你在调试工具中准确解读原始报文。
3.1 消息结构总览:Header + Body
一条完整的SECS消息由两部分组成:Message Header(消息头)和Message Body(消息体)。消息头固定长度为10字节(在HSMS模式下),而消息体则由一个或多个Data Item(数据项)构成。
调试要点: 在抓包工具(如Wireshark或SECS专用调试器)中,识别消息头的前10个字节是解析的第一步。任何长度不足或格式错误的头部都会导致会话异常。
3.2 Message Header 深度解析
消息头是SECS通信的“信封”,它告诉接收方这条消息从哪里来、要到哪里去、属于哪个会话以及如何重组。在HSMS协议中,10字节的Header结构如下:
| 字节偏移 | 字段名称 | 长度 | 描述 |
|---|---|---|---|
| 0-1 | Device ID / Session ID | 2 字节 | 设备标识符,用于区分总线上的不同设备。Bit 15 为控制位(0=数据消息,1=控制消息)。 |
| 2 | Stream (流) | 1 字节 | 消息类别,如 Stream 1 (设备控制)、Stream 6 (数据收集)。 |
| 3 | Function (功能) | 1 字节 | 具体功能码,如 S1F1 (Are You There)、S6F11 (Event Report)。 |
| 4 | PType (消息类型) | 1 字节 | 协议类型,HSMS 中通常为 0x00 (SECS-II)。 |
| 5 | SType (会话类型) | 1 字节 | 会话类型,0x00=数据消息,0x01=选择/分离等控制消息。 |
| 6-7 | Block Number (块号) | 2 字节 | 当前块编号,从 1 开始。单块消息为 0x8001。 |
| 8-9 | System Bytes (系统字节) | 2 字节 | 系统事务ID,用于匹配请求与响应。 |
抓包技巧: 在调试工具中,重点关注 Stream/Function 和 System Bytes。S/F 决定了消息的业务含义,而 System Bytes 用于追踪一次完整的“请求-响应”事务。如果响应超时,首先检查 System Bytes 是否匹配。
3.3 Data Item 的组成与格式
消息体由Data Item构成。每个Data Item包含三个部分:Length(长度)、Format(格式)和Value(值)。这种自描述的结构使得SECS协议能够灵活传输各种类型的数据。
3.3.1 Data Item 三要素
- Length (长度):1~3字节,表示Value部分的字节数。长度本身也使用可变长度编码(如长度小于128字节时用1字节表示)。
- Format (格式):1字节,高4位表示数据类型(如List、ASCII、Binary、U2、F4等),低4位表示字节数或子类型。
- Value (值):实际数据内容,长度由Length字段决定。
| 格式码 (高4位) | 数据类型 | 示例 | 调试工具显示 |
|---|---|---|---|
| 0x0 | List (列表) | 包含多个子Item | L, n |
| 0x1 | Binary (二进制) | 0x01 0x02 | B: 01 02 |
| 0x2 | Boolean (布尔) | TRUE / FALSE | BOOL: True |
| 0x4 | ASCII (字符串) | "Hello" | A: "Hello" |
| 0x8 | U1 / U2 / U4 (无符号整数) | 100, 1000 | U2: 1000 |
| 0x9 | I1 / I2 / I4 (有符号整数) | -50, 32000 | I4: -50 |
| 0xA | F4 / F8 (浮点数) | 3.14 | F4: 3.14 |
常见解析错误: 在抓包时,如果看到调试工具显示“Invalid Data Item”或长度异常,通常是因为 Format 字节 被误读,或者 Length 字段使用了多字节编码(如长度 > 127 时需占用2字节)。务必确认工具是否支持可变长度编码。
3.4 Block 与 Multi-Block 传输机制
当一条SECS消息的数据量超过底层传输单元(如TCP报文段)的承载能力时,协议会将消息分割成多个Block(块)进行传输。理解Block机制对于分析大消息(如配方上传、大量日志数据)至关重要。
3.4.1 单块 (Single Block) 消息
大多数控制类消息(如S1F1、S2F17)都是单块消息。其特点是:
- 消息头中的 Block Number 字段值为
0x8001(高位为1表示这是最后一块,同时也是第一块)。 - 整个消息体在一个TCP包中传输完毕。
3.4.2 多块 (Multi-Block) 消息
当消息体较大时,发送方将其拆分为多个Block依次发送。接收方根据Block Number进行重组。
| Block 序号 | Block Number 值 | 含义 |
|---|---|---|
| 第 1 块 | 0x0001 | 起始块,高位为0,表示后面还有更多块。 |
| 第 2 块 | 0x0002 | 中间块,序号递增。 |
| ... | ... | ... |
| 最后 1 块 | 0x800N | 结束块,高位为1,表示这是最后一块(N为总块数)。 |
调试关键: 在抓包分析中,如果发现消息不完整或重组失败,请检查:
1. 所有块的 System Bytes 是否一致?
2. Block Number 是否连续且没有丢包?
3. 最后一块的 Block Number 高位是否为 1?
1. 所有块的 System Bytes 是否一致?
2. Block Number 是否连续且没有丢包?
3. 最后一块的 Block Number 高位是否为 1?
3.5 抓包分析实战:识别Block边界
在Wireshark或SECS调试工具中,面对一串连续的TCP流,你需要快速识别出Block的边界。以下是标准步骤:
- 定位消息头: 找到10字节的Header起始位置。通常可以通过查找固定的PType (0x00) 和 SType (0x00) 来辅助定位。
- 读取Block Number: 解析字节6-7。如果值为
0x8001,则为单块消息,直接解析后续Body即可。 - 判断多块: 如果Block Number的高位为0(如
0x0001),则说明这是多块消息的第一块。记录下System Bytes,准备接收后续块。 - 等待最后一块: 持续接收具有相同System Bytes的后续块,直到收到Block Number高位为1的块(如
0x8003)。 - 重组与校验: 将所有块的Body部分按Block Number顺序拼接,然后解析完整的Data Item树。
工具推荐: 使用支持“自动重组”功能的调试工具(如SECSMon或某些商业调试器)可以大幅提高效率。它们会自动处理Block排序和重组,直接展示完整的消息结构树,让你专注于Data Item的值是否正确。
3.6 常见问题与避坑指南
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 消息解析为乱码 | Format字节解析错误,导致长度读取异常 | 检查Format字节的高4位,确认数据类型是否正确 |
| 多块消息重组超时 | 中间块丢失或顺序错乱 | 检查TCP层是否有重传或乱序;确认Block Number是否连续 |
| System Bytes不匹配 | 多块消息中混入了其他事务的块 | 过滤同一System Bytes的包,排除干扰 |
| Header长度不足10字节 | TCP粘包导致数据未完全到达 | 检查TCP载荷长度,确认是否需要进行应用层缓存 |