第 1 章:全景 —— 一个内核,为什么能长出六种产品
本章不急着进入协议细节。先回答三个问题:Transmission 到底是什么、代码为什么这样分层,以及后续应该抓住哪些核心对象。
一、一句话定义
Transmission 是以 libtransmission 为共享内核、以 C API 和 JSON-RPC 为控制边界、同时服务桌面端、服务端和远程客户端的 BitTorrent 产品族。
“BitTorrent 客户端”只描述了功能;“产品族”才揭示它的工程形态。仓库里至少能看到这些交付物:
| 产品/组件 | 入口目录 | 控制内核的方式 | 典型场景 |
|---|---|---|---|
transmission-daemon | daemon/ | 本地链接 C API,开放 RPC | NAS、服务器、路由器 |
transmission-gtk | gtk/ | 本地链接 C API | Linux 桌面 |
transmission-qt | qt/ | 本地或远程 JSON-RPC | Linux / Windows 桌面与远程控制 |
| macOS App | macosx/ | 本地链接 C API | 原生 macOS 桌面 |
| Web UI | web/ | 浏览器调用 JSON-RPC | 远程管理 |
transmission-remote | utils/remote.cc | 命令行调用 JSON-RPC | 脚本与运维 |
transmission-cli | cli/ | 本地链接 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_swarm与tr_peerMgr管“这个任务认识哪些 Peer、连谁、留谁、向谁请求”;tr_peerIo管连接字节流,tr_handshake和tr_peerMsgsImpl管协议状态;tr_io*、tr_verify_worker、tr_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:
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 timers3.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.0。libtransmission 目录有 174 个 .cc/.h 文件,约 6.5 万行,其中实现文件约 4.5 万行。最大的几个实现文件正好对应高复杂度聚合:
| 文件 | 约行数 | 为什么大 |
|---|---|---|
rpcimpl.cc | 3009 | RPC 字段映射与 24 个同步/异步方法 |
torrent.cc | 2711 | 单任务生命周期与统计聚合 |
peer-mgr.cc | 2682 | Peer 池、连接、rechoke、请求协调 |
peer-msgs.cc | 2266 | Peer Wire 与扩展消息状态机 |
session.cc | 2196 | 全局子系统装配与配置应用 |
announcer.cc | 1830 | Tracker tier、调度、失败切换 |
文件大不等于没有结构。Transmission 大量使用内部 helper namespace、Mediator、小型值类型和信号连接,把公开表面积控制得较小;但这些“大聚合文件”仍是维护成本的集中区。
六、读完本系列应形成的判断框架
遇到一个行为时,依次问:
- 状态属于 Session、Torrent、Swarm 还是 Peer?
- 当前代码运行在前端线程、Session 线程、验证线程还是 libcurl/libevent 回调中?
- 输入是可信的内部调用,还是来自 torrent 文件、Peer、Tracker、RPC 的不可信数据?
- 它受连接数、带宽、队列、文件句柄或定时器中的哪一种资源约束?
- 最终通过信号、回调、统计字段还是 RPC 被观察?
只要这五个问题能回答,陌生函数就能放回系统图,而不是孤立地阅读。
源码锚点
README.md:产品形态与构建入口CMakeLists.txt:版本、功能开关和子项目装配libtransmission/transmission.h:公共 C APIlibtransmission/session.h:Session 所有权图libtransmission/torrent.h:Torrent 领域状态docs/Transmission-Architecture.md:上游的产品架构说明