在 BT 下载过程当中,我们如果拿到一个种子文件,在其内部会包含一组 BT Tracker 服务器信息。在开始进行下载的时候,BT 下载工具会根据种子内的唯一 HASH 码请求 Tracker 服务器,之后 Tracker 服务器会返回给正在 下载/做种 的 Peer 信息,下载工具获得了其他的 Peer 信息之后就会与其他的 Peer 建立通讯下载数据。
整个过程的时序图如下:
在这里 BT Tracker 充当的角色就是一个通讯员的角色,它的构造很简单,最简构造的情况下只需要一个 HTTP API 接口即可。其作用就是在 BT 下载工具请求 Peer 信息的时候,返回相应的信息即可。
二、BT 协议与 BEncode 编码
在 BT 协议通讯的过程当中,所有的数据都是通过 B Encode 进行编码的。这种编码方式类似于 JSON 的数据组织形式,它可以表示字符串与整形这两种基本类型,也可以表示列表与字典这两种数据结构,其语法规则很简单。
字符串 "hello" 的编码形式:
[字符串长度]:[字符串数据]
5:hello
整数 10 的编码形式:
i[整数]e
i10e
列表这种数据结构,可以包含任意 B 编码的类型,包括 字符串、整形、字典(dictionary)、列表(list) 。
包含两个字符串元素 "hello"、"world" 的 列表 编码形式:
I[内容]e
I5:hello5:world
字典的概念与我们 C# 当中 BCL 所定义的 Dictionary<string,T> 一样,它是由一个键值对组成,其键的类型必须为 B 编码的字符串,而其值可以为任意的 B 编码类型,包括 字符串、整形、字典(dictionary)、列表(list) 。
在本篇文章的示例当中,没有自行编写 B Encode 的编码与解码工具类,而是使用的第三方库 BencodeNET 来进行操作。
当然,针对于 B Encode 的编解码工具类的编写并不复杂,有了上述的解析,你也可以尝试自己编写一个 B Encode 编解码工具类。
三、整体编写思路
BT Tracker 服务器本质上就是一个 Web Api 项目,BT 客户端携带种子的唯一 HASH 值,去请求某个接口,从而获得正在工作的 Peer 列表。剩下的事情就与 Tracker 服务器无关了,Tacker 服务器的职责就是为 BT 下载工具提供正在工作的其他 BT 客户端。
因此我们第一步就需要建立一个基于 .NET Core 的 Web Api 项目,并编写一个控制器接口用于响应 BT 下载工具的请求。除此之外,我们还需要一个字典用来存储种子与对应的 Peer 集合信息,在 BT下载工具请求 Tracker 服务器的时候,能够返回相应的 Peer 集合信息。
除了返回给 BT 下载工具 Peer 信息之外,Tracker 还可以根据 Client 请求时携带的附加数据来更新 Peer 的统计信息。(这些信息常用于 PT 站进行积分统计)
Tracker 服务器针对于返回的 Peer 集合有两种处理方式,第一种则是 紧凑模式 ,这个时候 Tracker 服务器需要将 Peer 的 IP 与 Port 按照 [IP 地址(4 byte)][端口号(2 byte)] 的形式进行编码,返回二进制流。另一种则是直接将 Peer 集合的信息,通过 BDictionary 进行编码,其组织形式如下。
{PeerIdKey,PeerId 值},
{IpKey,IP 值},
{PortKey,Port 值}
最后总结来说,如果要实现最简的 Tracker 服务器,只需要管理好 Peer (BT 客户端) 的状态,并且响应 Peer 的请求即可。如果需要实现积分制,那么就需要针对 Peer 的信息进行持久化处理。
四、BT Tacker 服务器接口的定义
BT 下载工具向 Tracker 接口请求的参数与返回的参数已经在 BT 协议规范 当中有说明,下面我们就来介绍一下请求参数与返回参数的含义。
4.1 请求参数
参数名称 具体含义 类型 必填
info_hash 种子的唯一 HASH 标识。 string 是
peer_id BT 下载端的唯一标识,由客户端生成。 string 是
ip 客户端的 IP 地址。 string 否
port 客户端监听的端口。 int 是
uploaded 客户端已经上传的数据大小,以 byte 为单位。 long 是
downloaded 客户端已经下载的数据大小,以 byte 为单位。 long 是
left 客户端待下载的数据大小,以 bytes 为单位。 long 是
event 当前事件,一般有三个值代表客户端现在的状态,已开始
、已停止、已完成。 string 是
compact 是否启用紧凑模式,如果为 1 则启动,为 0 则正常编码。 int 否
numWant 客户端想要获得的 Peer 数量。 int 否
Tracker 的接口返回参数其 Content-Type 的值必须为 text/plain ,并且其结果是通过 B Encode 进行编码的。最外层是一个 BDictionary 字典,内部的数据除了一个 Peer 集合之外,还包含了以下的固定键值对。
4.2 返回参数
字典键 字典值类型 含义 必填
peers BList/BString Peer 列表,根据 compact 参数不同,其值类型不一样。 是
interval BNumber 客户端发送规则请求到 Tracker 服务器之后的强制等待
时间,以秒为单位。 是
min interval BNumer 最小的发布时间间隔,客户端的重发间隔不能小于此值,也
是以秒为单位。 是
tracker id BString Tracker 服务器的 Id,用于标识服务器。 是
complete BNumber 当前请求的种子,已经完成的 Peer 数量(做种数)。 是
incomplete BNumber 当前请求的种子,非做种状态的用户。 是
failure reason BString Tracker 处理失败的原因,为可选参数。 否
五、编码实现 BT Tracker 服务器
5.1 基本架构
首先新建立一个标准的 Web API 模板项目,删除掉其默认的 ValuesController ,建立一个新的控制器,其名字为 AnnounceController ,最后我们的项目结构如下。
添加一个 GetPeersInfo 接口,其 HTTP Method 为 GET 类型,建立一个输入 DTO 其代码如下。
public class GetPeersInfoInput
{
/// <summary>
/// 种子的唯一 Hash 标识。
/// </summary>
public string Info_Hash { get; set; }
/// <summary>
/// 客户端的随机 Id,由 BT 客户端生成。
/// </summary>
public string Peer_Id { get; set; }
/// <summary>
/// 客户端的 IP 地址。
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 客户端监听的端口。
/// </summary>
public int Port { get; set; }
/// <summary>
/// 已经上传的数据大小。
/// </summary>
public long Uploaded { get; set; }
/// <summary>
/// 已经下载的数据大小。
/// </summary>
public long Downloaded { get; set; }
/// <summary>
/// 事件表示,具体可以转换为 <see cref="TorrentEvent"/> 枚举的具体值。
/// </summary>
public string Event { get; set; }
/// <summary>
/// 该客户端剩余待下载的数据。
/// </summary>
public long Left { get; set; }
/// <summary>
/// 是否启用压缩,当该值为 1 的时候,表示当前客户端接受压缩格式的 Peer 列表,即使用
/// 6 字节表示一个 Peer (前 4 字节表示 IP 地址,后 2 字节表示端口号)。当该值为 0
/// 的时候则表示客户端不接受。
/// </summary>
public int Compact { get; set; }
/// <summary>
/// 表示客户端想要获得的 Peer 数量。
/// </summary>
public int? NumWant { get; set; }
}
上面仅仅是 PT 客户端传递给 Tracker 服务器的参数信息,为了在后面我们方便使用,我们还需要将其转换为方便操作的充血模型。
用 .NET Core开发BT下载的Tracker服务器
于 2019-04-29 22:21:39 首次发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)