3. HSMS 协议详解:TCP/IP 传输层、连接建立与关闭、会话管理、消息格式、超时机制 (T3-T8)

HSMS(High-Speed SECS Message Services)是 SECS/GEM 协议族中基于 TCP/IP 的传输层协议。它将 SECS-II 消息封装为 HSMS 消息,通过可靠的 TCP 连接在主机与设备之间传输。本章深入解析 HSMS 的传输层特性、连接生命周期、会话管理、消息帧格式以及关键的 T3-T8 超时机制。

3.1 HSMS 协议栈与 TCP/IP 传输层

HSMS 直接运行在 TCP 之上,利用 TCP 的可靠传输、流量控制和拥塞控制机制,为上层 SECS-II 提供无差错、有序的字节流服务。与 RS-232 的 SECS-I 不同,HSMS 支持高速、长距离通信。

核心特性: HSMS 使用 TCP 端口号 5000(默认)或 5001(加密),采用主动/被动连接模式,支持多会话复用同一 TCP 连接。
HSMS 协议栈架构 SECS-II 消息层 HSMS 会话/消息层 TCP 传输层 IP 网络层 协议说明 • SECS-II: 消息内容与格式 • HSMS: 消息头、会话管理 • TCP: 可靠连接、字节流 • IP: 路由、寻址 • 默认端口: 5000/5001 • 最大消息长度: 4GB • 支持 IPv4/IPv6

3.2 连接建立与关闭

HSMS 定义了两种连接模式:主动(Active)和被动(Passive)。主动方发起 TCP 连接请求,被动方监听端口。连接建立后,双方通过 HSMS 控制消息完成会话初始化。

3.2.1 连接建立流程

  1. TCP 三次握手:主动方(通常是主机)向被动方(设备)发起 TCP 连接。
  2. 发送 Select.req:主动方发送 HSMS 控制消息,请求建立会话。
  3. 响应 Select.rsp:被动方回复 Select.rsp,携带通信能力协商结果。
  4. 连接就绪:双方进入通信状态,可以交换 SECS-II 数据消息。
测试要点:验证 Select.req 中的 Device ID 和通信能力字段是否与设备配置一致。不一致时,设备应返回 Select.rsp 且状态码为“拒绝”。
HSMS 连接建立与关闭时序 主动方 (Host) 被动方 (Equipment) 连接建立 TCP SYN TCP SYN+ACK TCP ACK 会话建立 Select.req Select.rsp (成功) 数据通信 Data Message (SECS-II) 连接关闭 Separate.req / TCP FIN

3.2.2 连接关闭方式

关闭方式 触发条件 行为描述
正常关闭 应用层调用断开 发送 Separate.req,对方回复 Separate.rsp,然后 TCP 四次挥手
超时关闭 T6/T7/T8 超时 直接发送 TCP RST 或 FIN,不等待应用层确认
异常关闭 TCP 连接中断 底层检测到连接断开,通知上层会话失效

3.3 会话管理

HSMS 会话(Session)是建立在 TCP 连接之上的逻辑通信通道。一个 TCP 连接可以承载多个会话,但实际应用中通常为一对一关系。会话通过 Device ID 唯一标识。

3.3.1 会话状态机

HSMS 会话状态机定义了 5 个状态:

  • NOT SELECTED:TCP 已连接,但未建立 HSMS 会话。
  • SELECTED:会话已建立,可以交换数据消息。
  • NOT CONNECTED:TCP 连接已断开。
  • SELECTING:正在发送 Select.req,等待响应。
  • WAITING:等待对方重新建立会话。
HSMS 会话状态机 NOT CONNECTED NOT SELECTED SELECTING SELECTED WAITING TCP 连接 发送 Select.req Select.rsp 成功 Separate.req 断开 超时

3.4 消息格式

HSMS 消息由 消息头(Header)和消息体(Body)组成。消息头固定 10 字节,消息体为可选的 SECS-II 数据。

3.4.1 消息头结构

字节偏移 字段 长度 描述
0 Length 4 字节 消息总长度(包含头+体),大端序
4 Device ID 2 字节 设备标识,bit15 为消息方向位
6 Stream/Function 2 字节 高字节 Stream,低字节 Function
8 P/B Type 1 字节 bit7: P-bit, bit6: W-bit, bit5-0: 消息类型
9 System Bytes 1 字节 系统字节(高 8 位),与后续 3 字节组成 4 字节 System ID
常见错误:Device ID 的 bit15 必须正确设置。bit15=0 表示消息从主机发往设备,bit15=1 表示从设备发往主机。错误的方向位会导致消息被丢弃。

3.4.2 消息类型

HSMS 定义了 7 种控制消息和 1 种数据消息:

消息类型 代码 方向 用途
Data Message 0 双向 传输 SECS-II 数据
Select.req 1 主动→被动 请求建立会话
Select.rsp 2 被动→主动 响应会话请求
Deselect.req 3 双向 请求释放会话
Deselect.rsp 4 双向 响应释放请求
Separate.req 9 双向 通知对方即将断开连接
Linktest.req 5 双向 测试连接是否存活
Linktest.rsp 6 双向 响应连接测试

3.5 超时机制 (T3-T8)

HSMS 定义了 6 个关键超时参数,用于保障通信的可靠性和异常处理。这些超时是 HSMS 一致性测试的核心验证点。

超时 名称 默认值 触发条件 超时后行为
T3 Reply Timeout 45 s 发送需要回复的消息后等待响应 重传或上报错误
T4 Select Timeout 10 s 发送 Select.req 后等待 Select.rsp 重试或断开连接
T5 Connect Timeout 10 s 主动方发起 TCP 连接后等待完成 连接失败,重试
T6 Separate Timeout 5 s 发送 Separate.req 后等待 Separate.rsp 直接关闭 TCP 连接
T7 Linktest Timeout 60 s 发送 Linktest.req 后等待 Linktest.rsp 断开连接
T8 Inter-Character Timeout 5 s TCP 字节流中相邻字节到达间隔 丢弃不完整消息
测试策略:超时测试是 HSMS 一致性测试的难点。建议使用以下方法:
1. 构造延迟响应,验证 T3/T4/T7 超时后的重传或断开行为。
2. 模拟 TCP 连接中断,验证 T5 超时后的重连机制。
3. 发送不完整消息,验证 T8 超时后的消息丢弃行为。
HSMS 超时机制作用域 时间 TCP 连接 T5 Select.req T4 数据消息 T3 Linktest.req T7 断开 T6 超时测试注意事项 • T3 超时:需验证重传次数和重传间隔是否符合规范(通常 3 次) • T4/T5 超时:超时后应进入 NOT CONNECTED 状态,并尝试重连 • T8 超时:仅适用于 TCP 字节流模式,不适用于消息模式 • 所有超时值应可通过配置修改,测试时需验证配置接口

3.6 一致性测试要点总结

针对 HSMS 协议的一致性测试,需要覆盖以下关键场景:

  • 连接建立:验证 TCP 三次握手、Select.req/rsp 交互、Device ID 和通信能力协商。
  • 消息格式:验证消息头各字段的正确性,包括 Length、Device ID、S/F、P/B Type、System Bytes。
  • 会话管理:验证状态机转换的正确性,包括 SELECTED、NOT SELECTED、NOT CONNECTED 等状态。
  • 超时处理:验证 T3-T8 超时后的行为,包括重传、断开、重连等。
  • 异常场景:验证非法消息、重复 Select、未授权断开等异常处理。
推荐工具:使用 Wireshark 抓包分析 HSMS 消息,配合自定义测试脚本模拟异常场景。对于自动化测试,建议使用 Python 的 socket 库直接构造 HSMS 消息帧。
# Python 示例:构造 HSMS Select.req 消息
import struct
import socket

def build_select_req(device_id=0, system_id=1):
    # 消息头固定 10 字节
    # Length: 10 (只有头,无体)
    length = 10
    # Device ID: bit15=0 (Host→Equipment)
    dev_id = device_id & 0x7FFF
    # Stream/Function: Select.req 使用 S0F1
    s_f = (0 << 8) | 1
    # P/B Type: bit7=0, bit6=0, type=1 (Select.req)
    pb_type = 1
    # System Bytes: 4 字节,这里只演示高 8 位
    sys_bytes = (system_id >> 24) & 0xFF
    header = struct.pack('!IHHB', length, dev_id, s_f, pb_type)
    header += struct.pack('!I', system_id)[:1]  # 只取高 8 位
    return header

# 发送示例
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.1.100', 5000))
msg = build_select_req()
sock.send(msg)
response = sock.recv(1024)
print(f'Received {len(response)} bytes')
sock.close()