3. 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。同时开多个客户端连接,各自独立打印会话状态,互不影响。
会话状态流转
每个连接对应一个 Session 对象,状态在 Connected 与 Selected 之间切换,连接断开后线程结束并释放资源。