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中的目标地址验证连接失败时的异常处理。
判定标准:程序正常退出且打印测试完成,同时被动连接线程无异常,即视为通过。