3. HSMS 被动模式监听与多客户端接入

HSMS 被动模式监听与多客户端接入 学习卡片

任务说明

实现一个 HSMS 被动模式(Passive)监听服务:监听 TCP 端口,接受多个设备(客户端)连接,为每个连接维护独立的会话状态,并处理设备发来的 Select.req(S1F1 之外的 HSMS 控制消息,Stream=0,Function=1),回复 Select.rsp(S0F2),使会话进入 Selected 状态。

HSMS 消息头固定 10 字节:SessionID(2) + Stream(1) + Function(1) + PType(1) + SType(1) + SystemBytes(4)。控制消息 Stream=0,SType 区分类型:Select.req=1,Select.rsp=2,Deselect.req=3,Deselect.rsp=4,Linktest.req=5,Linktest.rsp=6。

环境与依赖

  • Python 3.8+,仅用标准库 socket、threading、struct。
  • 无需第三方库。运行:python hsms_passive.py,默认监听 0.0.0.0:5000。

核心实现

import socket, struct, threading

HOST, PORT = "0.0.0.0", 5000
HEADER = struct.Struct(">HBBBBL")  # SessionID, Stream, Function, PType, SType, SystemBytes

# 每个连接独立会话状态
class Session:
    def __init__(self, conn, addr):
        self.conn, self.addr = conn, addr
        self.selected = False
        self.lock = threading.Lock()

def recv_exact(conn, n):
    buf = b""
    while len(buf) < n:
        chunk = conn.recv(n - len(buf))
        if not chunk:
            return None
        buf += chunk
    return buf

def handle(session):
    conn = session.conn
    try:
        while True:
            raw = recv_exact(conn, 10)
            if raw is None:
                break
            sid, stream, func, ptype, stype, sysb = HEADER.unpack(raw)
            if stream == 0 and stype == 1:          # Select.req
                with session.lock:
                    session.selected = True
                rsp = HEADER.pack(sid, 0, 2, 0, 2, sysb)  # Select.rsp
                conn.sendall(rsp)
                print(f"[{session.addr}] Select.req -> Selected")
            elif stream == 0 and stype == 3:        # Deselect.req
                with session.lock:
                    session.selected = False
                conn.sendall(HEADER.pack(sid, 0, 4, 0, 4, sysb))
            elif stream == 0 and stype == 5:        # Linktest.req
                conn.sendall(HEADER.pack(sid, 0, 6, 0, 6, sysb))
            else:
                print(f"[{session.addr}] data msg S{stream}F{func} selected={session.selected}")
    finally:
        conn.close()
        print(f"[{session.addr}] disconnected")

def main():
    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind((HOST, PORT))
    srv.listen(8)
    print(f"HSMS passive listening on {HOST}:{PORT}")
    while True:
        conn, addr = srv.accept()
        session = Session(conn, addr)
        print(f"accepted {addr}")
        threading.Thread(target=handle, args=(session,), daemon=True).start()

if __name__ == "__main__":
    main()

运行与验证

启动服务后,用下面脚本模拟一个设备发送 Select.req 并读取响应:

import socket, struct
H = struct.Struct(">HBBBBL")
s = socket.create_connection(("127.0.0.1", 5000))
s.sendall(H.pack(0xFFFF, 0, 1, 0, 1, 12345))   # Select.req
print(H.unpack(s.recv(10)))                     # 期望 (0xFFFF,0,2,0,2,12345)
s.close()

判定通过:服务端打印 Select.req -> Selected,客户端收到 Stream=0、Function=2、SType=2、SystemBytes 与请求一致的 Select.rsp。同时开多个客户端连接,各自独立打印会话状态,互不影响。

会话状态流转

Connected Selected Closed Select.req 断开 Deselect.req

每个连接对应一个 Session 对象,状态在 Connected 与 Selected 之间切换,连接断开后线程结束并释放资源。