跳到正文Skip to content

内部 shared 与 WebSocket 协议

当你要调试内部执行器、排查 shared/ws 模式的连接或序列化问题,或者评估把内部协议暴露到网络的后果时,这里说明两条内部链路的线上契约:shared 的 HTTP + pickle 帧协议,以及 ws 的 WebSocket + protobuf 任务协议。这里不重复三个服务模式的启动方式和模式级差异(见Web、WS 与 Shared 模式),也不重复 web 模式的端口与部署(见Web 服务器端口与部署);对外 HTTP API 的鉴权与错误见HTTP API 鉴权与错误

涉及的代码

  • shared 是内部 HTTP 执行器:MangaShareMangaTranslator 包装成 FastAPI 服务,默认监听 127.0.0.1:5003,只暴露三个受控端点,结果用 pickle 字节返回。它不是给浏览器访问的对外服务。
  • ws 是内部 WebSocket 执行器客户端:MangaTranslatorWS 继承翻译器,主动连接上游 ws://localhost:5000,用 x-secret 头认证,收发 protobuf WebSocketMessage 帧。解析器为它声明的本地 --host 127.0.0.1 --port 5003 --nonce 不被当前实现消费。
  • 两条链路都是内部协议:shared 默认只绑定回环地址,ws 默认只连接本地上游;nonce/secret 是仅有的鉴权手段,且以明文 HTTP/WebSocket 头传输;pickle 反序列化与 protobuf 解析都面对不可信输入风险。
  • 端口边界:web 对外 0.0.0.0:8000,shared 监听 127.0.0.1:5003,ws 连接上游 ws://localhost:5000500050038000 三者不能混写。

端口与端点契约

flowchart LR
    subgraph SharedSide["shared 内部执行器(127.0.0.1:5003)"]
        S1["GET /is_locked"]
        S2["POST /simple_execute/{method}"]
        S3["POST /execute/{method}"]
    end
    subgraph WsSide["ws 执行器"]
        W1["连接上游 ws://localhost:5000(x-secret)"]
        W2["GET source_image / PUT translation_mask"]
    end
    C["内部客户端"] --> S1
    C --> S2
    C --> S3
    UP["上游调度器"] --> W1
    W1 --> W2

shared 内部协议

MangaShare 是内部执行器:接收 JSON 请求、以线程锁串行执行翻译、用 pickle 返回结果或流式帧。所有执行端点按固定顺序做四道检查:nonce 校验(设置 nonce 后 X-Nonce 必须匹配,否则 401)、方法白名单(只允许 translatetranslate_batch,否则 403)、方法存在性(不存在 404)、非阻塞锁(忙时 429)。

请求编码

  • 单图:{"image": "<PNG base64>", "config": {...}};批量:{"images": ["<PNG base64>", ...], "config": {...}, "batch_size": n}
  • configConfig.model_dump(mode="json") 生成;图片由服务端 _decode_image 校验,非法图片、非法配置、非法批量请求分别返回 422Invalid image data / Invalid translation config / Invalid batch request)。
  • 客户端编码在 sent_data_internal.py_encode_image / _encode_config / _encode_attributes

流式帧格式

每个流帧是 状态(1 字节) + 长度(4 字节大端) + 载荷,客户端 extract_header / handle_buffer 按 5 字节头切帧:

flowchart LR
    H["1 字节 status"] --> L["4 字节长度(大端)"]
    L --> P["载荷(length 字节)"]
    P --> V0["status=0:pickle 结果"]
    P --> V1["status=1:UTF-8 进度"]
    P --> V2["status=2:错误"]

结果对象带 use_placeholder 时,服务端只回传 1×1 白色占位图的最小 Context,避免传输大图。

nonce 与访问控制

  • nonce 来源:shared --nonceargs.py),或 web 模式的 MT_WEB_NONCE 环境变量与启动时 secrets.token_hex(16) 生成(server/main.pyserver_utils.generate_nonce())。
  • MangaShare.check_nonce() 只比较请求头 X-Nonce 与自身 self.nonce--nonce 未设置时服务完全不校验。
  • nonce 是共享密钥,在启动日志中打印(Nonce: ...),并经明文 HTTP 头传输;泄露后等于没有鉴权。不得把日志中的 nonce 复制进公开报告。
  • /is_locked 不校验 nonce,其余两个执行端点都校验。

pickle 序列化风险

  • 结果序列化用 pickle.dumps,客户端用 pickle.loads 反序列化(sent_data_internal.py#fetch_datamode/share.py#run_method)。
  • pickle 不是安全格式:反序列化不可信数据可以执行任意代码(RCE)。只有两端都可信、数据未被篡改时才可用。
  • 后果:shared 只能作为内部执行器使用,不能暴露公网,也不应接受不可信调用方发送的请求。

WebSocket 内部协议

MangaTranslatorWS 以客户端身份工作:不监听端口,而是连接 --ws-url 上游,接收 new_task,翻译后把结果上传回上游给定的 translation_mask URL。

连接与鉴权

  • 连接:websockets.connect(url, extra_headers={'x-secret': secret}, max_size=1_000_000);消息上限 1 MB。
  • secret 来源:ws_secret 参数或 WS_SECRET 环境变量(mode/ws.py),默认空串;CLI ws 子命令没有 --ws-secret 选项。
  • x-secret 是明文 WebSocket 头;secret 为空时上游若放行则等同无鉴权。
  • Windows 上先调用 WSAStartup 并设置 ProactorEventLoopPolicy,连接循环运行在独立线程的 _server_loop 中。

任务生命周期

收到 new_task 后:发送 pending → 发送 downloading → HTTP GET source_image(失败发 error-download)→ 打开图片(长宽超过 1200 像素时强制 upscale_ratio=1)→ 发送 preparing → 在主循环执行翻译(进度 hook 经 0.2 秒节流器合并发送,状态为翻译器流水线阶段名)→ 结果非空时发送 saving,缩回原尺寸并转 PNG(verbose 另存 ws_final.png)→ 发送 uploading → HTTP PUT translation_mask(失败发 error-upload)→ 最终发送 finish_task

flowchart LR
    T["new_task"] --> P["pending"]
    P --> D{"GET source_image"}
    D -->|"失败"| ED["error-download"]
    D -->|"成功"| G["downloading"]
    G --> R["preparing → 翻译(进度帧节流 0.2s)"]
    R --> S["saving"]
    S --> U{"PUT translation_mask"}
    U -->|"失败"| EU["error-upload"]
    U -->|"成功"| F["finish_task"]
  • 所有任务经 PriorityLock 串行调度(task_lock((1 << 31) - ws_count));_run_text_translation 需要把翻译协程搬回 ctx.ws_event_loop 时,会先释放锁再按 (1 << 30) - ws_count 重新获取。
  • _run_text_rendering 计算渲染蒙版(输入蒙版 ∪ 输出变化像素),verbose 时写出 ws_render_in.pngws_render_out.pngws_mask.pngws_inmask.pngws_output.png;最终输出按蒙版裁剪为 RGBA。
  • translation_params(即 CLI 参数)只填充 params 中为 None 的默认值。

protobuf 风险与缺失模块

  • 客户端直接 ParseFromString(raw) 解析上游消息;max_size=1_000_000 是唯一的大小限制,字段合法性由生成代码决定。
  • 当前仓库未跟踪 manga_translator/server/ws_pb2.py 或对应 .proto 文件;listen()from ..server import ws_pb2ImportError,因此 ws 模式当前无法启动。这是源码差异,不是已验证的运行行为。
  • 恢复该模式需要重新生成 ws_pb2.py 并验证消息字段与上游调度器一致;在恢复前不要把 ws 当作可运行服务。

约束与注意事项

  • 端口边界:web 0.0.0.0:8000、shared 127.0.0.1:5003、ws 上游 ws://localhost:5000;三者用途不同,不互相覆盖。
  • ws --host/--port/--nonce 在解析器中存在,但 MangaTranslatorWS 不消费;不要根据帮助文本推断 ws 会监听 5003
  • shared/ws 是内部协议:不要用浏览器直接访问,不要暴露公网;nonce/secret 明文传输、pickle 反序列化与 protobuf 解析都携带安全风险。
  • web 模式强制 start_instance=False,不会自动拉起 shared 实例;server/main.pystart_translator_client_proc 属于旧路径,且访问/追加了 shared 子解析器未声明的 --ignore-errors--pre-dict--post-dict 选项,不能当作正式行为。
  • 当前仓库缺 ws_pb2.pyws 模式无法启动;这是源码差异。
  • 这里不读取或展示真实 .envWS_SECRET、nonce、API key、令牌、用户名或私有路径。

开发指南

选项中英对照

端口与端点契约

入口源码固定值说明与来源
shared 监听--host / --port 默认 127.0.0.1:5003manga_translator/args.pymode/share.py#MangaShare.listen() 用它启动内部 FastAPI
shared 端点GET /is_lockedPOST /simple_execute/{method_name}POST /execute/{method_name}mode/share.py#listen() 内联定义;方法白名单只放行 translatetranslate_batch
shared 长连接Uvicorn timeout_keep_alive=1800保持连接 30 分钟以支持批量翻译
ws 上游--ws-url 默认 ws://localhost:5000manga_translator/args.pymode/ws.py 读取 ws_url
ws 本地字段--host 127.0.0.1--port 5003--nonce解析器存在,MangaTranslatorWS 当前不消费
ws 边信道任务里的 source_image(HTTP GET)与 translation_mask(HTTP PUT)mode/ws.pyaiohttp 会话,30 秒超时
web 遗留注册POST /registerX-Nonce 头)server/main.py#register_instance;web 模式强制 start_instance=False,不自动拉起 shared

shared 内部协议

端点行为
GET /is_locked返回 {"locked": true/false};不做 nonce 校验
POST /simple_execute/{method_name}同步执行,成功返回 application/octet-stream 的 pickle 字节;失败返回 4xx/5xx
POST /execute/{method_name}流式执行,立即返回 application/octet-streamStreamingResponse,后台任务逐帧写入进度/结果

流式帧格式

status载荷
0结果:pickle 序列化的翻译结果
1进度:UTF-8 状态串(翻译器进度 hook 写入)
2错误:错误信息(当前为 Shared worker failed

消息结构

消息是 protobuf WebSocketMessagews_pb2),用 SerializeToString() / ParseFromString(raw) 编解码,WhichOneof('message') 区分三类:

消息字段用途
new_taskidtarget_languageskip_languagedetectordirectiontranslatorsizesource_imagetranslation_mask上游下发任务
statusidstatus执行器回报状态
finish_taskidsuccesshas_translation_mask任务结束

UI 文案对照

shared/ws 是服务器内部协议,桌面 UI 的 locale 文件中没有 “shared”“websocket”“5003” 等界面文案。与 CLI 开关共享的桌面设置标签如下:

UI 调用 keyEnglish 实际值简体中文实际值
label_use_gpuUse GPU使用 GPU
label_disable_onnx_gpuDisable ONNX GPU Acceleration禁用 ONNX GPU 加速
label_verboseVerbose Logging详细日志
label_attemptsRetry Attempts重试次数
label_ignore_errorsIgnore Errors忽略错误
文件/格式本页实际作用注意
manga_translator/mode/share.pyshared 执行器:端点、nonce、锁、pickle 与流帧X-Nonce 头、方法白名单、use_placeholder 优化
manga_translator/mode/ws.pyws 执行器:上游连接、x-secret、protobuf 消息、下载/上传ws_pb2 模块缺失
manga_translator/server/sent_data_internal.pyshared 客户端:base64 图片、config JSON、pickle 往返、流帧解析extract_header/handle_buffer
manga_translator/server/instance.pyExecutorInstancesent* 调用与 Executors 注册遗留路径
manga_translator/server/server_utils.pygenerate_nonce()、图片/JSON/字节转换secrets.token_hex(16)
manga_translator/server/main.pyweb 启动、nonce 生成、POST /registerstart_instance强制 start_instance=False
manga_translator/args.py__main__.pyws/shared 子解析器与模式分发帮助文案固定中文,不走 i18n
manga_translator/utils/threading.pyPriorityLockThrottlerws 任务调度与 0.2 秒节流
desktop_qt_ui/locales/en_US.jsonzh_CN.jsondoc/wiki/data/i18n.generated.jsonlabel_* 实际中英文无 shared/ws 专用 UI 文案

Mermaid 边界

上图描述代码中的端点、帧格式和任务状态流转,不代表 ws://localhost:5000 在本仓库内一定存在监听服务,也不代表 ws 模式当前能启动(ws_pb2.py 缺失)。shared 执行器分发是源码保留的旧路径,正式 web 模式不会自动拉起它。。

代码位置

层级文件本页核对内容
shared 服务manga_translator/mode/share.py三个端点、nonce、方法白名单、锁、pickle、帧状态码、use_placeholdertimeout_keep_alive
shared 客户端manga_translator/server/sent_data_internal.pyinstance.pyJSON 编码、pickle 往返、流帧切分、执行器注册
ws 执行器manga_translator/mode/ws.pyws_urlWS_SECRET/x-secret、protobuf 消息、任务状态机、节流与 PriorityLock、Windows 初始化
web 服务manga_translator/server/main.pyserver_utils.pynonce 生成与打印、/registerstart_instance=False、遗留启动命令
参数与分发manga_translator/args.py__main__.pyws/shared 选项与默认值、模式分发
工具manga_translator/utils/threading.pyPriorityLockThrottler
调查基线doc/wiki/research/cli-command-inventory.mdphase0-web-user-http.mdphase0-page-coverage-matrix.md--help 清单、端口/协议边界、ws_pb2.py 缺失记录