漫谈PT构架(4):框架结构及相关分析

Rhilip 2018-07-31 AM 1355℃ 0条

在实质性的进入Tracker内部逻辑之前,让我先水一篇分析性的文章~

框架结构

  • NexusPHP

像NP这种较老的框架并没有一个很明显的架构,各项信息杂糅在一起。但从导航栏相关中,大体可以分为以下几个子模块。

以下分类方式仅代表本人意见。

nexusphp_framework.jpg

当然,目前各站在这个基础上均增加了一些新的系统,比如(万恶的)勋章系统、发布预告系统、转种系统(含自引用与他站引用)、任务系统、考核系统;对原有系统也有些许扩展,例如Bangumi榜单、AniDB榜单等的添加。

  • meanTorrent

meanTorrent的可以在其modules下明确的了解其架构。撰稿时,架构如下:

meantorrent_framwork.jpg

Tracker请求生命周期

从整个请求流程来看,用户的Bittorrent客户端使用HTTP GET的形式向Tracker服务器发起请求,Tracker服务器根据内部逻辑向数据库及缓存请求请求相关信息,并最终回复给用户。具体流程可见下方简图:

lifetime.jpg

当然上面的肯定过于简单了,没有对Tracker的内部逻辑进行分析,而就Tracker的内部逻辑来看,以NP这个面向流程的框架为例子,简图如下:

tracker_life.jpg

而从整个Tracker网络(见下方图片示意)来看,用户在请求后,Tracker并不参与文件交换,同样,也不能真实的获取用户实际交换信息情况。(于是,我又可以就这个方向水一篇SP了)

main-qimg-ff65100c115e14ddf6c606b3799e0ae7.png

图片来自 Why is BitTorrent not downloading? - Quora

Tracker分析

在这一手记前,首先先列一下几个相关文件,以方便快速查阅:


首先从最简单的信息查询来看,BT客户端会在还未正式向 /announce 发送请求之前,会向 /scrape 先发送请求(一般在种子 添加或者校验的时候),请求头仅添加种子的info_hash信息,示例如下:

GET http://nexusphp.localhost/scrape.php?passkey=110099109e337b7e335aa368200d7907c&info_hash=%e4%40%ad%c6%27%db%02%40%f7%c8%ed%9e%7b%aab%a3%e1gFW HTTP/1.1
Host: nexusphp.localhost
User-Agent: uTorrent/2210(25302)
Accept-Encoding: gzip
Connection: Close

关于scrape的相关定义及返回信息在之前列出来的BEP 0048中,根据定义,这次抓取(scrape) 会向用户返回一个字典描述对应种子的完成、未完成以及下载中的情况。本处直接引述定义:

The response to a successful request is a bencoded dictionary containing one key-value pair: the key files with the value being a dictionary of the 20-byte string representation of an infohash paired with a dictionary of swarm metadata.

根据NP的实现(见 NexusPHP/scrape.php ),会从param字段中搜索并匹配出来info_hash信息,如果param中没有能匹配出info_hash,则会直接返回站内所有种子的做种信息。(如下图)

TIM截图20180730165317.jpg

以上问题修复patch见 :lock: Not allow empty info_hash when request scrape page by Rhilip · Pull Request #11 · zcqian/tjupt,截止目前,多数站点未对该问题进行修复。

而如果存在info_hash的话,则会对每一个提供的hash值进行数据库查找,如果查询结果不存在,则返回Torrent not registered with this tracker.的错误,而如果存在,则以一个benc编码的字典返回结果。其json格式如下:

{
  "files": {
    "xxxxxxxxxxxxxxxxxxxx": {"complete": 11, "downloaded": 13772, "incomplete": 19},
    "yyyyyyyyyyyyyyyyyyyy": {"complete": 21, "downloaded": 206, "incomplete": 20}
  }
}

此外,一个正常的客户端(例如Transmission 2.94)会定期向Tracker发送(announce)以下信息,并期望从tracker中获取回复以获取其他peer信息(这里仅指Private Tracker,其他的还有DHT网络、用户交换等方式)。

GET http://nexusphp.localhost/announce.php?passkey=db534098baaaa68bd725aaaae3051518&info_hash=aaaaaaaaaaaaaaaaaaaa&peer_id=-TR2940-lhqkh1jtwjqp&port=21736&uploaded=0&downloaded=0&left=739573325&corrupt=0&key=DDDD2A2B&event=started&numwant=200&compact=1&no_peer_id=1 HTTP/1.1
Host: nexusphp.localhost
User-Agent: Transmission/2.94
Accept-Encoding: gzip
Connection: Close

而tracker则会根据GET字段中的compact以及no_peer_id两个字段分别返回不同构造的结果,并设置相应头为

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Pragma: no-cache

compact=0&no_peer_id=0时,其返回的json格式如下:

{
    "interval": 50,
    "min interval": 20,
    "complete": 6871,
    "incomplete": 3,
    "peer": [
        {
            "ip": "xxx.xxxx.xxx.xxx",
            "peer_id": "-TR2940-lhqkh1j31jqp",
            "port": 5698,
        },
        ....
    ]
}

其中, peer_id 视用户Requests字段这一项可以不要,而如果用户申请使用compact的形式返回,那么peer字段则会返回一个string而不是dict。

如果发生错误,则会返回一个错误字段(string),JSON格式内容如下:

{
    "failure reason": "Error msg",
}

Bencode编码

在上面的例子中,我都是以json格式的形式来展示tracker返回的Response信息,但实际上,返回的信息是以Bencode的形式进行编码的。关于Bencode的介绍,可以访问Bencode - Wikipedia。这里我们只需要知道一下编码规则就行:

data typesrawencoded
int-42i-42e
string'spam'4:spam
list['XYZ', 4321]l3:XYZi4321ee
dict{'XYZ': 4321}'d3:XYZi4321ee

这些甚至不需要你自己一个一个拼合,现在有超多的库帮你进行拼合,你只需要构造好相应的字典,直接调用即可,例如:

非特殊说明,本博所有文章均为博主原创。

评论啦~