66 lines
1.9 KiB
Python
66 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
wire.py — 会话消息的打包 / 解包。
|
||
|
||
pynng 一条消息只能是单块二进制,所以这里做一层很简单的外壳:
|
||
b"LS01" | u32 头长度 | JSON 头(UTF-8) | 二进制体
|
||
|
||
每个头里都带 session:
|
||
{
|
||
"id": "<一次运行一个 uuid>",
|
||
"client_ip": "192.168.43.23", # 电脑自己的地址
|
||
"board_ip": "192.168.43.50", # 板子的地址
|
||
"started_ns": 1712345678901234567,
|
||
"note": "..."
|
||
}
|
||
|
||
这样每一条请求和应答都能看出属于哪个会话、从哪台机器来。
|
||
"""
|
||
import json
|
||
import struct
|
||
import time
|
||
import uuid
|
||
|
||
MAGIC = b"LS01"
|
||
_LEN = struct.Struct("<I")
|
||
|
||
|
||
def new_session(board_ip, client_ip, note=""):
|
||
"""新建一个会话(电脑端每次启动调一次)。"""
|
||
return {
|
||
"id": uuid.uuid4().hex,
|
||
"client_ip": str(client_ip),
|
||
"board_ip": str(board_ip),
|
||
"started_ns": time.time_ns(),
|
||
"note": str(note),
|
||
}
|
||
|
||
|
||
def pack(header, blob=b""):
|
||
"""dict + 二进制体 -> bytes。"""
|
||
h = json.dumps(header, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
||
return MAGIC + _LEN.pack(len(h)) + h + bytes(blob)
|
||
|
||
|
||
def unpack(data):
|
||
"""bytes -> (dict, 二进制体)。"""
|
||
if len(data) < 8 or data[:4] != MAGIC:
|
||
raise ValueError("bad frame magic")
|
||
n = _LEN.unpack_from(data, 4)[0]
|
||
if len(data) < 8 + n:
|
||
raise ValueError("truncated header")
|
||
header = json.loads(data[8:8 + n].decode("utf-8"))
|
||
return header, data[8 + n:]
|
||
|
||
|
||
def session_id(header):
|
||
return (header.get("session") or {}).get("id")
|
||
|
||
|
||
def check_session(header, session):
|
||
"""应答必须属于当前会话,防止串台。"""
|
||
got = session_id(header)
|
||
if got != session["id"]:
|
||
raise RuntimeError("session mismatch: got %r, want %r" % (got, session["id"]))
|