2. 实现HSMS连接建立

任务说明

本案例实现HSMS(High-Speed Message Service)连接建立的核心逻辑,包括主动连接(Active Connect)和被动连接(Passive Connect)两种模式,并完成TCP/IP连接与会话建立。HSMS是SEMI标准中基于TCP/IP的通信协议,用于设备与主机之间的消息交换。

实现方案

使用Python实现一个最小可运行的HSMS连接模块。核心包括:

  • 主动连接:本端作为客户端,向对端发起TCP连接,随后发送Select.req消息建立会话。
  • 被动连接:本端作为服务端,监听端口接受连接,收到Select.req后回复Select.rsp完成会话建立。
  • 消息格式:HSMS消息由消息头(10字节)和正文组成,消息头包含会话ID、流/功能码等字段。

完整代码

import socket
import threading
import struct
import time

# HSMS消息头长度
HSMS_HEADER_LEN = 10

def build_select_req(session_id=0xFFFF):
    """构造 Select.req 消息(流0 功能码1)"""
    # 消息头: 长度(4字节) + SessionID(2字节) + 头字节(1) + Stream(1) + Function(1) + PType(1) + SMType(1)
    # Select.req: Stream=0, Function=1, PType=0, SMType=1(会话管理)
    body = b''  # Select.req 无正文
    length = HSMS_HEADER_LEN + len(body)
    header = struct.pack('>IHBBBB', length, session_id, 0, 0, 1, 0, 1)
    return header + body

def build_select_rsp(session_id=0xFFFF):
    """构造 Select.rsp 消息(流0 功能码2)"""
    # Select.rsp 正文: 1字节状态 (0=成功)
    body = struct.pack('>B', 0)
    length = HSMS_HEADER_LEN + len(body)
    header = struct.pack('>IHBBBB', length, session_id, 0, 0, 2, 0, 1)
    return header + body

def parse_message(data):
    """解析HSMS消息,返回 (session_id, stream, function, body)"""
    if len(data) < HSMS_HEADER_LEN:
        raise ValueError('消息长度不足')
    length, session_id, _, stream, function, ptype, smtype = struct.unpack('>IHBBBBB', data[:HSMS_HEADER_LEN])
    body = data[HSMS_HEADER_LEN:length]
    return session_id, stream, function, body

def active_connect(host, port, timeout=5):
    """主动连接:发起TCP连接并发送Select.req,等待Select.rsp"""
    sock = socket.create_connection((host, port), timeout=timeout)
    # 发送 Select.req
    sock.sendall(build_select_req())
    # 等待响应
    sock.settimeout(timeout)
    resp = sock.recv(1024)
    session_id, stream, function, body = parse_message(resp)
    if stream == 0 and function == 2 and body == b'\x00':
        print(f'[主动连接] 会话建立成功, session_id=0x{session_id:04X}')
        return sock
    else:
        sock.close()
        raise ConnectionError(f'Select.rsp 异常: stream={stream}, func={function}, body={body}')

def passive_connect(host, port):
    """被动连接:监听端口,接受连接并处理Select.req"""
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((host, port))
    server.listen(1)
    print(f'[被动连接] 监听 {host}:{port} ...')
    conn, addr = server.accept()
    print(f'[被动连接] 接受来自 {addr} 的连接')
    # 接收 Select.req
    data = conn.recv(1024)
    session_id, stream, function, body = parse_message(data)
    if stream == 0 and function == 1:  # Select.req
        # 回复 Select.rsp
        conn.sendall(build_select_rsp(session_id))
        print(f'[被动连接] 会话建立成功, session_id=0x{session_id:04X}')
        return conn, server
    else:
        conn.close()
        server.close()
        raise ConnectionError(f'收到非Select.req消息: stream={stream}, func={function}')

# 示例:被动连接在独立线程中运行
if __name__ == '__main__':
    HOST, PORT = '127.0.0.1', 5000

    # 启动被动连接(服务端)
    server_thread = threading.Thread(target=lambda: passive_connect(HOST, PORT), daemon=True)
    server_thread.start()
    time.sleep(0.5)

    # 主动连接(客户端)
    sock = active_connect(HOST, PORT)
    sock.close()
    print('测试完成')

运行与验证

运行方式:直接执行 python hsms_connect.py。程序会先启动被动连接线程监听端口,再发起主动连接。

验证方法:

  • 观察输出是否包含[被动连接] 监听 127.0.0.1:5000、[被动连接] 接受来自...、[主动连接] 会话建立成功等日志。
  • 若输出包含会话建立成功且无异常抛出,说明TCP连接建立、Select.req/rsp消息交换和会话建立均正常。
  • 可通过修改PORT为未占用端口验证监听行为;或修改active_connect中的目标地址验证连接失败时的异常处理。

判定标准:程序正常退出且打印测试完成,同时被动连接线程无异常,即视为通过。