BT源代码学习心得(八):跟踪服务器(Tracker)的代码分析(用户请求的实际处理)
发信人: wolfenstein (NeverSayNever), 个人文集
标 题: BT源代码学习心得(八):跟踪服务器(Tracker)的代码分析(用户请求的实际处理)
发信站: 水木社区 (Wed Aug 10 21:52:50 2005), 文集
标 题: BT源代码学习心得(八):跟踪服务器(Tracker)的代码分析(用户请求的实际处理)
发信站: 水木社区 (Wed Aug 10 21:52:50 2005), 文集
(本文包含HTML标记,终端模式下可能无法正确浏览)
通过上一次的分析,我们已经知道了Tracker采用http协议和客户端通信,这一次我们
就可以直接分析Tracker.get函数的代码,看看跟踪服务器是如何处理用户的请求的。
首先是检查IP,一个是通过网络连接直接得到的IP(这个有可能是对方的http代理服务
器的IP),另一个是从请求的头部数据中解析出来的IP,通过分析函数get_forwarded_ip和
_get_forwarded_ip我们可以发现,它的原理是从头部数据中看有没有这些关键字:
http_x_forwarded_for,http_client_ip,http_via,http_from,如果有的话,就说明当
前的http连接中的网络的另一头是一个代理服务器,而不是实际的客户端,有些http代理服
务器会提交这些http请求的头部数据告诉服务器客户端的真实IP地址。还有就是要检查这些
IP地址是否合法,是否为本地地址(10.*.*.*,127.*.*.*,169.254.*.*,
172.16.*.*-172.31.*.*,192.168.*.*)等,然后确认真实的IP值。接下来准备好
paramslist准备处理请求参数,这是一个字典,但是它的每个元素的值是一个列表,这种情
况在http请求的参数列表中很常见。
接下来用urlparse把用户的http请求分成标准的几部分,例如,类似如下的请求:
[ http://xxx.xxx.xxx/path;parameters?query1=a&query2=b&query3=c#fragment ]
http://xxx.xxx.xxx/path;parameters?query1=a&query2=b&query3=c#fragment
将被分割成这样的6块:http,xxx.xxx.xxx,path,parameters,
query1=a&query2=b&query3=c,fragment
接下来把query中的值按照'&'以及'='进行处理,把它们填入到paramslist中,确认出
通过上一次的分析,我们已经知道了Tracker采用http协议和客户端通信,这一次我们
就可以直接分析Tracker.get函数的代码,看看跟踪服务器是如何处理用户的请求的。
首先是检查IP,一个是通过网络连接直接得到的IP(这个有可能是对方的http代理服务
器的IP),另一个是从请求的头部数据中解析出来的IP,通过分析函数get_forwarded_ip和
_get_forwarded_ip我们可以发现,它的原理是从头部数据中看有没有这些关键字:
http_x_forwarded_for,http_client_ip,http_via,http_from,如果有的话,就说明当
前的http连接中的网络的另一头是一个代理服务器,而不是实际的客户端,有些http代理服
务器会提交这些http请求的头部数据告诉服务器客户端的真实IP地址。还有就是要检查这些
IP地址是否合法,是否为本地地址(10.*.*.*,127.*.*.*,169.254.*.*,
172.16.*.*-172.31.*.*,192.168.*.*)等,然后确认真实的IP值。接下来准备好
paramslist准备处理请求参数,这是一个字典,但是它的每个元素的值是一个列表,这种情
况在http请求的参数列表中很常见。
接下来用urlparse把用户的http请求分成标准的几部分,例如,类似如下的请求:
[ http://xxx.xxx.xxx/path;parameters?query1=a&query2=b&query3=c#fragment ]
http://xxx.xxx.xxx/path;parameters?query1=a&query2=b&query3=c#fragment
将被分割成这样的6块:http,xxx.xxx.xxx,path,parameters,
query1=a&query2=b&query3=c,fragment
接下来把query中的值按照'&'以及'='进行处理,把它们填入到paramslist中,确认出
请求的参数。下面就是根据各种情况返回给用户适当的结果了。
首先,如果用户请求路径是空的或者是'index.html'(对应于 [ http://xxx.xxx.xxx/
] http://xxx.xxx.xxx/或者 [ http://xxx.xxx.xxx/index.html ]
http://xxx.xxx.xxx/index.html),那么就返回给用户一个信息页面,使用get_infopage函
数完成,当然,在实际运行中,可以把配置文件设置为不允许显示信息页面,这样
get_infopage就会返回HTTP404代码,否则,生成一个页面文件,它包含了一些基本信息,
以及该跟踪服务器目前关注的种子文件的列表以及它们的一些统计情况。接下来的情况是用
户请求路径'scrape'或者'file',也是返回相应的信息给用户。其中,get_scrape返回的各
个种子文件的统计信息,例如某个种子有多少下载完了,多少人还没下载完成等。而'
file'则是直接获取某个种子文件(即允许用户直接从跟踪服务器下载种子文件)。注意在以
上的请求过程中,表示某个具体的种子文件的关键字是它的信息部分的消息摘要值
(infohash)。
在处理完另一种情况,用户要求返回图标文件后,接下来就是跟踪服务器的主要功能,
announce了。所以如果路径不是announce的话,那就返回HTTP404。下面先获取infohash,
即客户端请求的是哪个种子文件的情况,然后进行check_allowed检查,排除有些种子不能
在此跟踪服务器上下载的情况。这样可以及时得对跟踪服务器做一些授权方面的操作,例如
如果发现有人发布了有违反国家法律法规的内容的种子,跟踪服务器的维护人员只需要及时
得把这个种子的infohash添加到这个列表中,就再没有人可以下载这个种子了。
在排除了各种意外的情况后,就可以用add_data把用户的信息添加进去了。一个跟踪服
务器在获取了一个客户端要求下载某个种子文件的请求后,它把这个客户的信息记录起来,
这样他就知道有哪些客户对某个具体的种子文件感兴趣。然后再根据这些列表,选取一些客
户的信息返回给客户,而要下载这个种子文件中的实际共享资源?自己去找其它客户(客户
首先,如果用户请求路径是空的或者是'index.html'(对应于 [ http://xxx.xxx.xxx/
] http://xxx.xxx.xxx/或者 [ http://xxx.xxx.xxx/index.html ]
http://xxx.xxx.xxx/index.html),那么就返回给用户一个信息页面,使用get_infopage函
数完成,当然,在实际运行中,可以把配置文件设置为不允许显示信息页面,这样
get_infopage就会返回HTTP404代码,否则,生成一个页面文件,它包含了一些基本信息,
以及该跟踪服务器目前关注的种子文件的列表以及它们的一些统计情况。接下来的情况是用
户请求路径'scrape'或者'file',也是返回相应的信息给用户。其中,get_scrape返回的各
个种子文件的统计信息,例如某个种子有多少下载完了,多少人还没下载完成等。而'
file'则是直接获取某个种子文件(即允许用户直接从跟踪服务器下载种子文件)。注意在以
上的请求过程中,表示某个具体的种子文件的关键字是它的信息部分的消息摘要值
(infohash)。
在处理完另一种情况,用户要求返回图标文件后,接下来就是跟踪服务器的主要功能,
announce了。所以如果路径不是announce的话,那就返回HTTP404。下面先获取infohash,
即客户端请求的是哪个种子文件的情况,然后进行check_allowed检查,排除有些种子不能
在此跟踪服务器上下载的情况。这样可以及时得对跟踪服务器做一些授权方面的操作,例如
如果发现有人发布了有违反国家法律法规的内容的种子,跟踪服务器的维护人员只需要及时
得把这个种子的infohash添加到这个列表中,就再没有人可以下载这个种子了。
在排除了各种意外的情况后,就可以用add_data把用户的信息添加进去了。一个跟踪服
务器在获取了一个客户端要求下载某个种子文件的请求后,它把这个客户的信息记录起来,
这样他就知道有哪些客户对某个具体的种子文件感兴趣。然后再根据这些列表,选取一些客
户的信息返回给客户,而要下载这个种子文件中的实际共享资源?自己去找其它客户(客户
称呼其它客户时应该叫对等客户)解决吧,跟踪服务器上一个字节都没有。
add_data首先从downloads这个字典中找到关键字为infohash的项,赋值到peers中,如
果downloads中没有这项,则新建这项(setdefault,详见python库参考手册之内建类型字典
的详细说明),然后检查传进来的参数是否合法,如peerid必须是20个字节,event(如果有
)只能是'started','completed','stopped','snooped'中的一项等。peers是一个以
peerid为关键字的字典,因此先看看原来有没有这个peer,用peerid把它取出来。然后准备
好rsize,这个是返回的项(即返回多少个对等客户的信息给客户)的数目,它由客户的要求
以及服务器的配置参数共同决定。然后如果用户的请求中有event=stopped项,则删除该客
户的信息,否则,如果peer为空(peers中没有这个peerid的peer),则设置好一个新的peer
的信息,然后添加进去,如果peer已经存在了,那么就更新一下它的信息。最后返回rsize
完成任务。其中有一个NAT处理的类,定义在BitTorrent/NatCheck.py中,它通过往用户声
称的IP和端口进行一次连接,看看有没有反应,以确认NAT情况,并且记录下来。最后还要
把这些数据放到becache中,这个地方存放经过一定格式处理后的客户数据,可以加快返回
客户信息的函数的处理过程。becache中所有的客户信息都按照下载完成(做种)和未完成分
开了,这个可以方便服务器返回一定的做种peers和非做种peers给客户。
在add_data把客户的数据更新好后,接下来就是用peerlist函数返回一定量的客户信息
了。它根据用户的要求(rsize),以及所有的peers中种子数占的比例,决定返回给客户的信
息中,有多少是做种的peers,即返回给客户的做种peers占所有返回给客户的peers的比例
,应该接近于所有在下载这个种子的做种peers和全部peers的比例。然后把两部分的信息复
制到一个cache中,再用shuffle把它们打乱,最后把这些信息返回给客户。因此我们可以看
到,跟踪服务器返回用户信息的准则是,在保持种子所占的比例接近全局的种子比例的情况
下,随机选取客户信息返回给客户。
add_data首先从downloads这个字典中找到关键字为infohash的项,赋值到peers中,如
果downloads中没有这项,则新建这项(setdefault,详见python库参考手册之内建类型字典
的详细说明),然后检查传进来的参数是否合法,如peerid必须是20个字节,event(如果有
)只能是'started','completed','stopped','snooped'中的一项等。peers是一个以
peerid为关键字的字典,因此先看看原来有没有这个peer,用peerid把它取出来。然后准备
好rsize,这个是返回的项(即返回多少个对等客户的信息给客户)的数目,它由客户的要求
以及服务器的配置参数共同决定。然后如果用户的请求中有event=stopped项,则删除该客
户的信息,否则,如果peer为空(peers中没有这个peerid的peer),则设置好一个新的peer
的信息,然后添加进去,如果peer已经存在了,那么就更新一下它的信息。最后返回rsize
完成任务。其中有一个NAT处理的类,定义在BitTorrent/NatCheck.py中,它通过往用户声
称的IP和端口进行一次连接,看看有没有反应,以确认NAT情况,并且记录下来。最后还要
把这些数据放到becache中,这个地方存放经过一定格式处理后的客户数据,可以加快返回
客户信息的函数的处理过程。becache中所有的客户信息都按照下载完成(做种)和未完成分
开了,这个可以方便服务器返回一定的做种peers和非做种peers给客户。
在add_data把客户的数据更新好后,接下来就是用peerlist函数返回一定量的客户信息
了。它根据用户的要求(rsize),以及所有的peers中种子数占的比例,决定返回给客户的信
息中,有多少是做种的peers,即返回给客户的做种peers占所有返回给客户的peers的比例
,应该接近于所有在下载这个种子的做种peers和全部peers的比例。然后把两部分的信息复
制到一个cache中,再用shuffle把它们打乱,最后把这些信息返回给客户。因此我们可以看
到,跟踪服务器返回用户信息的准则是,在保持种子所占的比例接近全局的种子比例的情况
下,随机选取客户信息返回给客户。
今天把跟踪服务器的代码全部分析完了,下一次就可以开始客户端源代码分析了。