# pymsg 模块技术手册 > 文档版本:2026-07-30 > 代码版本:0.1.0 > 语言:Python 3.9+(核心)、C++(gateway 集成)、Go(waf 集成) > 定位:跨语言 TCP 消息帧协议库 --- ## 一、模块概述 ### 1.1 定位 **pymsg** 是一个极简跨语言 TCP 消息帧协议库,基于长度前缀(4 字节大端 uint32)+ UTF-8 载荷。为微服务体系提供轻量级 RPC 通信层。 ### 1.2 设计哲学 | 原则 | 说明 | |------|------| | 零依赖 | 仅使用 Python stdlib,无需任何第三方包 | | 极简协议 | 4 字节头 + 载荷,实现只需 20 行代码 | | 跨语言 | Python/C++/Go 三端实现完全兼容 | | 二进制安全 | 长度前缀帧,JSON 中的换行符无需转义 | | fail-safe | 日志推送在 db 不可用时静默丢弃,不影响业务 | ### 1.3 与微服务体系的关系 pymsg 是微服务的**核心通信层**: ``` ┌──────────────┐ pymsg ┌──────────────┐ │ dnfs:8006 │ ───────→│ db:8005 │ │ (Python) │ 短连接 │ (Python) │ └──────────────┘ RPC └──────────────┘ ┌──────────────┐ pymsg ┌──────────────┐ │ gateway:8443 │ ───────→│ db:8005 │ │ (C++20) │ 短连接 │ │ └──────────────┘ RPC └──────────────┘ ┌──────────────┐ pymsg ┌──────────────┐ │ waf:443 │ ───────→│ db:8005 │ │ (Go) │ 短连接 │ │ └──────────────┘ RPC └──────────────┘ ``` --- ## 二、技术架构 ### 2.1 协议格式 ``` ┌────────────────────────┬──────────────────────────┐ │ 4 字节(大端序) │ N 字节(UTF-8) │ │ uint32 载荷字节长度 │ JSON / 文本载荷 │ └────────────────────────┴──────────────────────────┘ ``` ### 2.2 架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ 微服务调用方 │ │ (dnfs / gateway / waf / static / smtp / captcha) │ └─────────────────────────┬───────────────────────────────────┘ │ MsgClient.call() / send() ┌─────────────────────────▼───────────────────────────────────┐ │ pymsg 协议层 │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 底层协议函数 │ │ │ │ send(sock, data) → [4字节长度头][UTF-8载荷] │ │ │ │ recv(sock) → 读4字节→解析长度→读对应字节→返回 │ │ │ └──────────────────────────┬──────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────▼──────────────────────────────┐ │ │ │ MsgClient 客户端 │ │ │ │ send(msg) — 短连接,发送即忘(带 name 前缀) │ │ │ │ call(msg) — 短连接,RPC 请求-应答 │ │ │ │ listen(cb) — 长连接,回调模式 │ │ │ └──────────────────────────┬──────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────▼──────────────────────────────┐ │ │ │ MsgServer 服务端 │ │ │ │ start(on_message) — 启动监听 │ │ │ │ reply(sock, msg) — 回复客户端 │ │ │ │ broadcast(msg) — 广播到所有客户端 │ │ │ │ stop() — 停止服务 │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ TCP ┌─────────────────────────▼───────────────────────────────────┐ │ db:8005 服务端 │ │ MsgServer + 路由分发 │ └─────────────────────────────────────────────────────────────┘ ``` ### 2.3 消息流 #### RPC 请求-应答 ``` 调用方 服务端 (db) ────── ────────── MsgClient.call(json_str) │ ├── socket.connect(127.0.0.1:8005) ├── send(sock, json_str) │ ├── payload = json_str.encode("utf-8") │ ├── header = struct.pack("!I", len(payload)) │ └── sock.sendall(header + payload) │ ├── recv(sock) ←── on_message(sock, addr, text) │ ├── sock.recv(4) ├── json.loads(text) │ ├── length = struct.unpack("!I", header)[0] │ ├── sock.recv(length) ├── handler(req) │ └── payload.decode("utf-8") └── server.reply(sock, response) │ ├── send(sock, response) ├── close(sock) │ └── sock.sendall(header + payload) └── return response ←── recv(sock) 返回 response ``` #### 日志推送 ``` 各服务 (gateway/static/smtp) db:8005 ───────────────────────────── ────────── log_client.push(entry) │ ├── 入内存队列 (ch, 容量 256) │ ├── 后台 goroutine 批量 flush │ ├── 满 50 条 或 2 秒超时 │ └── 逐条 MsgClient.call(json) │ └── (同 RPC 流程,fail-safe:失败静默丢弃) ``` --- ## 三、代码结构 ### 3.1 文件结构 ``` pymsg/ ├── pymsg/ │ └── __init__.py # 核心实现(~370 行) ├── README.md # 用户文档 ├── pyproject.toml # 项目配置 └── .gitignore ``` ### 3.2 跨语言实现位置 | 语言 | 文件 | 说明 | |------|------|------| | Python | [pymsg/__init__.py](file:///Users/qingsong/micro_servers/pymsg/pymsg/__init__.py) | 主实现 | | C++ | [gateway/inc/Msg.hpp](file:///Users/qingsong/micro_servers/gateway/inc/Msg.hpp) | gateway 使用 | | Go | [waf/internal/logclient/logclient.go](file:///Users/qingsong/micro_servers/waf/internal/logclient/logclient.go) | waf 使用 | | Python (黑盒) | [tests/blackbox/helpers.py](file:///Users/qingsong/micro_servers/tests/blackbox/helpers.py) | 纯 stdlib 测试实现 | ### 3.3 代码索引 #### Python 实现(__init__.py) | 函数/类 | 行号 | 功能 | |---------|------|------| | `send(sock, data, *, timeout=None)` | L48 | 底层发送 | | `recv(sock, *, timeout=None)` | L72 | 底层接收 | | `_recv_exact(sock, n)` | L103 | 精确读 n 字节 | | `_timeout(sock, seconds)` | L34 | 超时 context manager | | `TimeoutError` | L30 | 超时异常 | | `MsgClient` | L120 | TCP 客户端 | | `MsgClient.__init__` | L136 | 构造器 | | `MsgClient.connect()` | L144 | 建立长连接 | | `MsgClient.send(msg)` | L150 | 短连接发送(带 name 前缀) | | `MsgClient.call(msg, *, timeout=None)` | L163 | 短连接 RPC | | `MsgClient.listen(on_message)` | L193 | 长连接监听 | | `MsgClient.stop()` | L220 | 停止监听 | | `MsgServer` | L234 | TCP 服务端 | | `MsgServer.start(on_message)` | L259 | 启动服务 | | `MsgServer.broadcast(msg, except_sock)` | L296 | 广播 | | `MsgServer.reply(sock, msg)` | L321 | 回复 | | `MsgServer.stop()` | L329 | 停止服务 | #### C++ 实现(Msg.hpp) | 函数/类 | 行号 | 功能 | |---------|------|------| | `MsgException` | L47 | 基础异常 | | `MsgTimeoutException` | L52 | 超时异常 | | `MsgConnectionException` | L57 | 连接异常 | | `writeUint32BE(buf, val)` | L72 | 4 字节大端编码 | | `readUint32BE(buf)` | L79 | 4 字节大端解码 | | `setSockTimeout(sockfd, seconds)` | L88 | 设置 socket 超时 | | `recvExact(sockfd, buf, n)` | L99 | 精确读 n 字节 | | `sendAll(sockfd, data, len)` | L121 | 完整发送 | | `openConnection(addr, port, timeout)` | L139 | 打开短连接 | | `pmsg_send(sockfd, data)` | L174 | 协议发送 | | `pmsg_recv(sockfd)` | L188 | 协议接收 | | `MsgClient` | L210 | C++ 客户端 | | `MsgClient.connect()` | L220 | 长连接 | | `MsgClient.send(msg)` | L228 | 短连接发送 | | `MsgClient.call(msg, timeout)` | L248 | 短连接 RPC | | `MsgClient.listen(on_message)` | L269 | 长连接监听 | | `MsgClient.stop()` | L311 | 停止 | #### Go 实现(logclient.go) | 函数 | 行号 | 功能 | |------|------|------| | `pymsgCall(payload)` | L226 | 发送并接收一条 pymsg 消息 | | `Client.sendOne(entry, ts)` | L179 | 发送单条日志 | | `Client.send(batch)` | L171 | 批量发送 | | `Client.flushLoop()` | L125 | 后台 flush goroutine | | `Push(service, level, message, extra)` | L82 | 全局推送 | --- ## 四、API 参考 ### 4.1 底层函数 #### send(sock, data, *, timeout=None) ```python def send(sock: socket.socket, data: str, *, timeout: float | None = None) -> None ``` **功能**:在已连接的 socket 上发送一条长度前缀帧消息 - 将字符串编码为 UTF-8 - 前面加上 4 字节大端长度头 - 调用 `sock.sendall()` 发送 **参数**: - `sock`:已连接的 socket 对象 - `data`:要发送的字符串 - `timeout`:可选超时(秒),超时抛出 `TimeoutError` #### recv(sock, *, timeout=None) ```python def recv(sock: socket.socket, *, timeout: float | None = None) -> str ``` **功能**:从已连接的 socket 接收一条长度前缀帧消息 - 读取 4 字节头解出长度 - 读取指定字节数 - 返回解码后的 UTF-8 字符串 **参数**: - `sock`:已连接的 socket 对象 - `timeout`:可选超时(秒) **返回值**:解码后的字符串 **异常**: - `ConnectionError`:连接在完整消息接收前断开 - `TimeoutError`:操作超时 #### TimeoutError ```python class TimeoutError(Exception) ``` `send` 或 `recv` 超过指定超时时抛出的异常。 --- ### 4.2 MsgClient(客户端) #### 构造 ```python client = MsgClient(addr: str, port: int, name: str) ``` **参数**: - `addr`:服务端地址或 IP - `port`:服务端端口 - `name`:客户端标识(`send()` 模式下以 `"name: message"` 格式发送) #### 方法 | 方法 | 模式 | 说明 | |------|------|------| | `connect()` | 长连接 | 创建持久 TCP 连接(供 `listen()` 使用) | | `send(msg)` | 短连接 | 发送即忘,消息带 `name: msg` 前缀 | | `call(msg, timeout=10.0)` | 短连接 | RPC 模式,阻塞等待回复 | | `listen(on_message)` | 长连接 | 阻塞当前线程,持续接收消息 | | `stop()` | — | 中断 `listen()`,可从另一线程调用 | #### 使用示例 ```python from pymsg import MsgClient # RPC 调用(短连接) client = MsgClient("127.0.0.1", 8005, "my-service") resp = client.call('{"_route":"execute","action":"select","table":"Users"}', timeout=10.0) print(resp) # {"success": true, "data": [...]} # 发送即忘 client.send("hello") # 长连接监听 def on_message(text): print(f"收到: {text}") client.connect() client.listen(on_message) # 阻塞,直到 stop() ``` --- ### 4.3 MsgServer(服务端) #### 构造 ```python server = MsgServer(addr: str, port: int) ``` **参数**: - `addr`:监听地址(`0.0.0.0` 表示所有网卡) - `port`:监听端口 #### 方法 | 方法 | 说明 | |------|------| | `start(on_message)` | 启动服务器(阻塞),回调签名:`on_message(sock, addr, text)` | | `reply(sock, msg)` | 向指定客户端回复 | | `broadcast(msg, except_sock=None)` | 广播到所有客户端(可排除某个 socket) | | `stop()` | 停止服务(可从另一线程调用) | #### 回调签名 ```python def on_message(sock: socket.socket, addr: tuple, text: str): """ Args: sock: 发送消息的客户端套接字(用于 reply) addr: (ip, port) 元组 text: 解码后的字符串消息 """ server.reply(sock, "ack") ``` #### 使用示例 ```python from pymsg import MsgServer def on_message(sock, addr, text): print(f"[{addr}] {text}") server.reply(sock, "ack") server = MsgServer("0.0.0.0", 8005) server.start(on_message) # 阻塞 ``` --- ### 4.4 异常速查 | 异常 | 触发条件 | |------|----------| | `pymsg.TimeoutError` | `send()` 或 `recv()` 超过 `timeout` | | `ConnectionError`(内置) | 完整消息接收前 socket 断开 | | `ConnectionRefusedError`(内置) | `MsgClient.call()` 或 `send()` 无法连接目标 | | `RuntimeError` | `MsgClient.listen()` 在 `connect()` 之前调用 | --- ## 五、使用场景 ### 5.1 场景一:数据库服务(db:8005) db 服务通过 pymsg 暴露通用 CRUD 接口: ```python import json from pymsg import MsgServer class DBServer: def __init__(self): self._server = MsgServer("127.0.0.1", 8005) self._routes = {"execute": self._handle_execute} def start(self): self._server.start(self._dispatch) def _dispatch(self, sock, addr, text): req = json.loads(text) route = req.pop("_route", "") handler = self._routes.get(route) if handler is None: self._server.reply(sock, json.dumps({"success": False, "error": "未知路由"})) return try: result = handler(req) self._server.reply(sock, json.dumps({"success": True, "data": result})) except Exception as e: self._server.reply(sock, json.dumps({"success": False, "error": str(e)})) def _handle_execute(self, data): # 通过 SQLAlchemy 执行 SQL,返回 dict 列表 pass ``` ### 5.2 场景二:RPC 调用(dnfs → db) ```python import json from pymsg import MsgClient def _call_dbv2(payload): client = MsgClient("127.0.0.1", 8005, "dnfs") resp = client.call(json.dumps(payload), timeout=10.0) return json.loads(resp) # 创建笔记 result = _call_dbv2({ "_route": "execute", "action": "insert", "table": "Notes", "values": {"title": "会议记录", "content": "..."} }) ``` ### 5.3 场景三:异步日志推送(log_client) ```python import json from pymsg import MsgClient def _send(batch): for entry in batch: client = MsgClient("127.0.0.1", 8005, "gateway-log") client.call(json.dumps({ "_route": "execute", "action": "insert", "table": "Logs", "values": { "ts": now, "service": entry["service"], "level": entry["level"], "message": entry["message"], } }), timeout=5) ``` ### 5.4 场景四:日志推送(Go 实现) ```go // waf/internal/logclient/logclient.go func (c *Client) sendOne(entry Entry, ts string) { req := dbV2Request{ Route: "execute", Action: "insert", Table: "Logs", Values: values, } data, _ := json.Marshal(req) resp, err := pymsgCall(data) // 短连接 RPC if err != nil { return // fail-safe } } func pymsgCall(payload []byte) ([]byte, error) { conn, err := net.DialTimeout("tcp", "127.0.0.1:8005", 5*time.Second) // ... 写 4 字节头 + 载荷,读回复 } ``` --- ## 六、跨语言实现对照 ### 6.1 协议层 | 功能 | Python | C++ | Go | |------|--------|-----|-----| | 4 字节编码 | `struct.Struct("!I")` | `writeUint32BE` (手动位移) | `binary.BigEndian.PutUint32` | | 4 字节解码 | `struct.unpack("!I", header)` | `readUint32BE` (手动位移) | `binary.BigEndian.Uint32` | | 超时设置 | `_timeout` context manager | `setSockTimeout` (setsockopt) | `net.DialTimeout` + `SetDeadline` | | 安全上限 | 无 | 64 MiB | 无 | ### 6.2 客户端对照 | 操作 | Python | C++ | Go | |------|--------|-----|-----| | 构造 | `MsgClient(addr, port, name)` | `MsgClient(addr, port, name)` | — | | 长连接 | `connect()` | `connect()` | — | | 发送即忘 | `send(msg)` 短连接 + `name:` 前缀 | `send(msg)` 短连接 + `name:` 前缀 | — | | RPC 调用 | `call(msg, timeout)` 短连接 | `call(msg, timeout)` 短连接 | `pymsgCall(payload)` 短连接 | | 长监听 | `listen(on_message)` | `listen(on_message)` | — | | 停止 | `stop()` | `stop()` | — | ### 6.3 服务端对照 | 操作 | Python | C++ | Go | |------|--------|-----|-----| | 启动 | `start(on_message)` | — | — | | 回复 | `reply(sock, msg)` | `pmsg_send(fd, msg)` | — | | 广播 | `broadcast(msg, except_sock)` | — | — | | 停止 | `stop()` | — | — | ### 6.4 从头实现协议 仅需四步: ``` 1. 将字符串编码为 UTF-8 字节 2. 将字节长度打包为 4 字节大端无符号整数 3. 写入 头 + 载荷 到 socket 4. 接收端先读 4 字节解出长度,再读对应字节数,解码为 UTF-8 ``` --- ## 七、线程安全 | 场景 | 安全性 | 说明 | |------|--------|------| | `MsgServer.stop()` 跨线程调用 | ✅ 安全 | 设置原子标志并关闭监听 socket | | `MsgServer.reply()` 在回调内调用 | ✅ 安全 | 同一线程内调用 | | `MsgServer.broadcast()` 在回调内调用 | ✅ 安全 | 同一线程内调用 | | `MsgClient.stop()` 跨线程调用 | ✅ 安全 | 可中断 `listen()` | | `send()`/`recv()` 多线程共享 socket | ⚠️ 不安全 | 调用方需自行串行化 | | `MsgServer` 并发调用 broadcast/reply | ⚠️ 不支持 | 仅在 `on_message` 回调内(同一线程)安全 | --- ## 八、性能特性 ### 8.1 性能特点 | 特性 | 说明 | |------|------| | 零拷贝 | 长度前缀帧避免数据复制 | | 短连接复用 | `call()` 每次新建连接,但调用完成立即释放 | | fail-safe | 日志推送失败静默丢弃,不阻塞业务 | | 批量缓冲 | 日志推送:50 条或 2 秒自动 flush | ### 8.2 连接模型 | 模式 | 连接数 | 延迟 | 适用场景 | |------|--------|------|---------| | `send()` | 每次 1 个 | 中 | 低频通知 | | `call()` | 每次 1 个 | 中 | RPC 调用 | | `listen()` | 持久 1 个 | 低 | 高频消息流 | --- ## 九、已知限制 | ID | 描述 | 严重等级 | 影响范围 | |----|------|---------|---------| | L1 | **无消息校验**:协议无 CRC/哈希,传输损坏无法检测 | 🟡 Medium | 所有消息 | | L2 | **无认证**:任何人可连接 8005 端口,无鉴权机制 | 🔴 High | db 服务 | | L3 | **无加密**:明文传输,无 TLS/加密保护 | 🔴 High | 所有服务 | | L4 | **Python 版无安全上限**:C++ 版有 64 MiB 上限,Python 版未检查 | 🟡 Medium | Python 客户端 | | L5 | **无重试机制**:连接失败直接抛异常,无自动重试 | 🟢 Low | 所有调用 | | L6 | **无版本号**:协议升级无兼容路径 | 🟢 Low | 长期维护 | | L7 | **无心跳**:长连接无 keep-alive,可能被防火墙切断 | 🟡 Medium | listen 模式 | | L8 | **全局阻塞**:`MsgServer.start()` 阻塞主线程,需另开线程 | 🟢 Low | 服务端 | ### 建议改进 1. **L2/L3**:在防火墙层面限制 8005 端口仅允许本机连接(当前部署已这样做) 2. **L4**:在 Python `recv()` 中添加消息大小上限检查 3. **L7**:在 `listen()` 循环中添加 ping/pong 机制 4. **L5**:在 `call()` 中添加简单重试(1-2 次) --- ## 十、设计考量 ### 10.1 为什么用长度前缀帧? | 方案 | 优点 | 缺点 | |------|------|------| | **长度前缀帧** ✅ | 二进制安全、实现简单、无需流式解析 | 无 | | 换行分隔 | 简单 | JSON 中换行需转义、缓冲区管理复杂 | | HTTP | 成熟、有工具 | 头部开销大、需额外依赖 | | Protobuf | 高效、类型安全 | 需额外依赖、跨语言实现复杂 | ### 10.2 为什么用短连接? | 考虑 | 说明 | |------|------| | 简单性 | 短连接实现最简洁,无连接管理开销 | | 可靠性 | 每次调用都是独立连接,单个请求失败不影响其他 | | 性能 | 本地回环(`127.0.0.1`)下 TCP 连接建立开销很小 | | 可测试性 | 易于模拟各种网络故障 | ### 10.3 协议设计取舍 | 取舍 | 选择 | 原因 | |------|------|------| | 头大小 | 4 字节 | 支持最大 4 GiB 消息,足够使用 | | 字节序 | 大端 | 网络标准序,跨语言一致 | | 编码 | UTF-8 | 支持中文/多语言,与 JSON 兼容 | | 版本号 | 无 | 保持最简,升级时可扩展 | --- ## 十一、开发指南 ### 11.1 安装 ```bash # 开发模式 cd pymsg pip install -e . # 作为本地依赖 # 在其他项目的 requirements.txt 中: # ../pymsg ``` ### 11.2 添加新功能 #### 添加消息校验(CRC) ```python import zlib def send_with_crc(sock, data): payload = data.encode("utf-8") crc = zlib.crc32(payload) & 0xFFFFFFFF # [4字节长度][4字节CRC][UTF-8载荷] header = _HEADER.pack(len(payload) + 4) crc_bytes = struct.pack("!I", crc) sock.sendall(header + crc_bytes + payload) ``` #### 添加心跳机制 ```python import time def listen_with_heartbeat(self, on_message, interval=30): last_recv = time.time() while self._running: # 发送心跳 if time.time() - last_recv > interval: send(self._sock, "PING") # 接收消息 text = recv(self._sock, timeout=1.0) if text == "PONG": continue last_recv = time.time() on_message(text) ``` ### 11.3 扩展协议版本 ```python # 在头中添加版本号 _VERSION = 1 # 1 字节版本号 def send_v2(sock, data): payload = data.encode("utf-8") header = struct.pack("!BI", _VERSION, len(payload)) sock.sendall(header + payload) def recv_v2(sock): header = _recv_exact(sock, 5) version, length = struct.unpack("!BI", header) if version != _VERSION: raise ValueError(f"Unsupported protocol version: {version}") payload = _recv_exact(sock, length) return payload.decode("utf-8") ``` ### 11.4 构建与测试 ```bash # Python 测试 cd pymsg python -c " from pymsg import MsgClient, MsgServer, send, recv # ... 单元测试 " # 黑盒测试 cd tests/blackbox python test_runner.py ``` --- ## 十二、版本历史 | 版本 | 日期 | 主要变更 | |------|------|---------| | 0.1.0 | 当前 | 初始版本:核心协议、MsgClient、MsgServer、跨语言实现 | --- ## 附录 ### A. 协议规范(RFC 风格) ``` Protocol: pymsg v0 Frame Format: ┌──────────┬──────────┬──────────────────┐ │ 4 bytes │ N bytes │ │ ├──────────┼──────────┼──────────────────┤ │ uint32 │ UTF-8 │ │ │ big-end │ string │ │ │ payload │ payload │ │ │ length │ │ │ └──────────┴──────────┴──────────────────┘ Encoding: UTF-8 Byte Order: Big-Endian (network byte order) Max Message Size: 2^32 - 1 bytes (theoretical) Timeout: Optional, specified per operation Modes: - Short Connection (send/call): connect → send → recv → close - Long Connection (listen): connect → loop(recv → callback) ``` ### B. 错误码映射 | 异常 | 来源 | 场景 | |------|------|------| | `TimeoutError` | pymsg | 操作超过指定超时 | | `ConnectionError` | Python | 连接在接收前断开 | | `ConnectionRefusedError` | Python | 目标服务不可达 | | `RuntimeError` | Python | 在 `connect()` 前调用 `listen()` | | `MsgTimeoutException` | C++ | C++ 版超时 | | `MsgConnectionException` | C++ | C++ 版连接异常 | ### C. 参考实现清单 | 语言 | 文件 | 类型 | |------|------|------| | Python | [pymsg/__init__.py](file:///Users/qingsong/micro_servers/pymsg/pymsg/__init__.py) | 主实现 | | Python | [tests/blackbox/helpers.py](file:///Users/qingsong/micro_servers/tests/blackbox/helpers.py) | 测试客户端 | | C++ | [gateway/inc/Msg.hpp](file:///Users/qingsong/micro_servers/gateway/inc/Msg.hpp) | gateway 客户端 | | Go | [waf/internal/logclient/logclient.go](file:///Users/qingsong/micro_servers/waf/internal/logclient/logclient.go) | waf 客户端 | ### D. 参考资源 - [Beej's Guide to Network Programming](https://beej.us/guide/bgnet/) — Socket 编程经典 - [The SO_REUSEADDR socket option](https://man7.org/linux/man-pages/man2/socket.2.html) - [Python socket module](https://docs.python.org/3/library/socket.html) --- *本文档基于 pymsg 源码分析生成,供开发团队参考。*