功能
- 多文件torrent下载
- 支持UDP/HTTP Tracker
支持DHT,PeX,磁力链接
Golang实现
找peers
Torrent文件结构与Bencode编码
一个torrent文件包含一个文件列表和关于所有片段的完整性元数据(integrity metadata),并可选地包含一个很长的tracker列表,并由Bencode编码。
Bencode编码支持4种数据类型:byte string,integer,list和dictionary。
以电影《早餐俱乐部》为例,其torrent文件结构如下所示:
|
|
在这个字典中,所有键和字符串类型值的结构都是length:xxx
,整型表示为ixxxe
,列表为l[]...[]e
,字典为d(key:[])...(key:[])e
,其中,[]
可以为四种类型的任意一种。
announce
:tracker的URLannounce-list
:tracker的URL列表(可选)info
:包含文件共享信息的字典files
:文件字典列表(仅当共享多个文件时)length
:文件的大小,以字节为单位path
:与子目录名称对应的字符串列表,其中最后一个是实际的文件名
name
:建议要保存的文件名(单文件)/ 建议的目录名称(多文件)piece length
:文件片的大小,通常为256 KBpieces
:每个文件片的二进制编码的SHA-1哈希列表(如果有多文件,则按文件字典中的顺序拼接在一起)
一个torrent文件是由infohash唯一标识的,其值为整个info
的SHA-1哈希。
解析torrent文件
解析bencode编码的torrent文件,本质是递归下降。当然,Marshal/Unmarshal的过程需要借助go的反射。最终解析得到一个扁平化的结构体TorrentFile
。
|
|
从tracker获取peers信息
根据Announce
中URL的协议类型,可将tracker分为UDP/HTTP tracker。
HTTP
想要得到peers信息,需要给tracker发送一个GET请求。参数列表如下:
info_hash
:torrent文件中info
字典(bencode编码)的SHA-1哈希peer_id
:客户端随机生成的20字节长度字符串port
:客户端监听的端口uploaded
:当前已上传量downloaded
:当前已下载量left
:当前剩余下载量
tracker收到请求后返回一个bencode编码的字典。
|
|
其中,interval
表示请求tracker的时间间隔,peers
包含了peer的IP地址和端口信息(大端表示),其值为每6字节一组的二进制数据。同样,需要解析得到peer信息切片。
UDP
为了减少HTTP请求给tracker服务器带来的开销、提升性能,tracker也支持UDP。
与HTTP的一次请求响应不同,由于UDP不可靠,客户端至少需要发送两个UDP数据包。为了保证安全性,tracker在收到客户端的第一个包时返回一个connection_id
,让客户端在下一个包带上该参数,收到后校验。同时,connection_id
一段时间后会失效。
Connect
首先,客户端发送connect包,参数如下:
protocol_id
:0x41727101980,用于标识协议action
:0 表示connect请求transaction_id
:随机值,4Bytes
tracker服务器返回的包:
action
:0 表示connect请求(如果出错则为3)transaction_id
:与客户端请求值一致connection_id
:由tracker生成,用于标识客户端
Announce
随后,客户端发送announce包,参数如下:
connection_id
:tracker返回的connection_idaction
:1 表示announce请求transaction_id
:随机值,4Bytesevent
:none = 0, completed = 1, started = 2, stopped = 3key
:随机值,4Bytesnum_want
:想要tracker返回的最大peers数量,-1为默认值
剩余的info_hash
,peer_id
这些参数与HTTP请求tracker时相同。
最后,tracker服务器返回包含peers信息的包:
action
:1 表示announce请求transaction_id
:与客户端请求值一致interval
:下一次客户端announce的时间间隔leechers
:未完成下载的peers数量seeders
:已经完成下载且愿意继续上传的peers数量
包的最后是6字节一组的peers信息(IP + Port)。