Skip to content

第 1 章:全景 —— 一个内核,为什么能长出六种产品

本章不急着进入协议细节。先回答三个问题:Transmission 到底是什么、代码为什么这样分层,以及后续应该抓住哪些核心对象。

一、一句话定义

Transmission 是以 libtransmission 为共享内核、以 C API 和 JSON-RPC 为控制边界、同时服务桌面端、服务端和远程客户端的 BitTorrent 产品族。

“BitTorrent 客户端”只描述了功能;“产品族”才揭示它的工程形态。仓库里至少能看到这些交付物:

产品/组件入口目录控制内核的方式典型场景
transmission-daemondaemon/本地链接 C API,开放 RPCNAS、服务器、路由器
transmission-gtkgtk/本地链接 C APILinux 桌面
transmission-qtqt/本地或远程 JSON-RPCLinux / Windows 桌面与远程控制
macOS Appmacosx/本地链接 C API原生 macOS 桌面
Web UIweb/浏览器调用 JSON-RPC远程管理
transmission-remoteutils/remote.cc命令行调用 JSON-RPC脚本与运维
transmission-clicli/本地链接 C API兼容性保留的单任务 CLI

它们不是各自实现下载逻辑。Tracker、DHT、Peer 协议、磁盘校验、限速和恢复都集中在 libtransmission

二、四层结构

2.1 产品壳层:负责交互,不拥有协议真相

前端决定窗口、菜单、列表刷新和本地偏好,但 torrent 的真实状态仍来自内核。GTK 和 macOS 直接持有 tr_session*;Web 与 transmission-remote 只能看到 RPC;Qt 特别值得注意,它既可在进程内启动 Session,也可连接远端 daemon。

2.2 控制层:两种门面

libtransmission/transmission.h 是稳定的 C 风格 API,屏蔽内部 C++ 类型和文件组织。rpc-server.cc + rpcimpl.cc 则把这套能力映射成 JSON-RPC 2.0。前者适合同进程 GUI,后者适合跨进程和跨机器控制。

2.3 核心层:状态与机制分开

  • tr_session 持有全局配置、事件线程、定时器、RPC、DHT、Announcer、Peer Manager、文件池等;
  • tr_torrent 持有单任务元数据、完成度、目录、限速、队列、统计和信号;
  • tr_swarmtr_peerMgr 管“这个任务认识哪些 Peer、连谁、留谁、向谁请求”;
  • tr_peerIo 管连接字节流,tr_handshaketr_peerMsgsImpl 管协议状态;
  • tr_io*tr_verify_workertr_resume 管磁盘事实与可恢复状态。

2.4 平台层:把差异压到边缘

文件、socket、子进程和 watch directory 都有 POSIX/Win32 或 inotify/kqueue/generic 实现。核心逻辑依赖抽象接口和编译期开关,而不是在每个业务函数里堆平台条件。

三、最重要的两个对象

3.1 tr_session 是运行时容器

Session 不是普通“配置对象”。它的生命周期等价于一个 Transmission 内核实例:创建专属事件线程,启动监听 socket,构造 Announcer/DHT/LPD/RPC 等服务,加载 torrents,并在关闭时按依赖顺序停止它们。

可以把它理解成手工实现的 application container:

text
tr_session
├── session_thread + libevent base
├── settings + stats + session id
├── torrents + torrent queue
├── announcer + DHT + LPD
├── peer manager + top bandwidth
├── RPC server + HTTP web fetcher
├── port forwarding + blocklist
├── open file pool + verify worker
└── recurring timers

3.2 tr_torrent 是聚合根

Torrent 不只是 .torrent 文件的内存表示。它把“元数据是什么、文件在哪里、哪些块已有、是否运行、属于哪个队列、限速多少、何时完成、要不要继续做种”收进一个有锁、有信号、有状态转换的聚合对象。

许多看似分散的行为最终回到它:收到块后调用 on_block_received();piece 校验成功后调用 on_piece_completed();启动时经过 start()start_in_session_thread();磁力链接补齐 metadata 后调用 set_metainfo()

四、从一次下载看全系统

这条链路揭示了后续章节的组织原则:不是“网络模块一章、工具函数一章”,而是围绕生命周期上的责任交接。

五、关键规模与版本事实

当前分析快照的顶层 CMake 标记为 4.2.0-dev,要求 C11 与 C++20;RPC 语义版本是 6.1.0libtransmission 目录有 174 个 .cc/.h 文件,约 6.5 万行,其中实现文件约 4.5 万行。最大的几个实现文件正好对应高复杂度聚合:

文件约行数为什么大
rpcimpl.cc3009RPC 字段映射与 24 个同步/异步方法
torrent.cc2711单任务生命周期与统计聚合
peer-mgr.cc2682Peer 池、连接、rechoke、请求协调
peer-msgs.cc2266Peer Wire 与扩展消息状态机
session.cc2196全局子系统装配与配置应用
announcer.cc1830Tracker tier、调度、失败切换

文件大不等于没有结构。Transmission 大量使用内部 helper namespace、Mediator、小型值类型和信号连接,把公开表面积控制得较小;但这些“大聚合文件”仍是维护成本的集中区。

六、读完本系列应形成的判断框架

遇到一个行为时,依次问:

  1. 状态属于 Session、Torrent、Swarm 还是 Peer?
  2. 当前代码运行在前端线程、Session 线程、验证线程还是 libcurl/libevent 回调中?
  3. 输入是可信的内部调用,还是来自 torrent 文件、Peer、Tracker、RPC 的不可信数据?
  4. 它受连接数、带宽、队列、文件句柄或定时器中的哪一种资源约束?
  5. 最终通过信号、回调、统计字段还是 RPC 被观察?

只要这五个问题能回答,陌生函数就能放回系统图,而不是孤立地阅读。

源码锚点

文档采用 CC BY-SA 4.0;源码片段保留 Transmission 上游许可。