posts - 56,  comments - 12,  trackbacks - 0

前言:
 自从7月份写完“客户端源码分析之五:Encoder 与 Connection 类”后,我就停止了继续对BT源码的分析。原因很多,最主要的还是懒惰吧。临到岁末,终于下定决心,无论如何,要完成这一系列的文章,对自己也算有个交待。
  前面的几篇文章,都是深入到源码的某一部分细节之中,虽然很清晰,但无助于读者对整体构架的把握(其实我自己当时也比较糊涂)。这一次重新开始读源码,重 点顺着几条线索往下读,感觉原来杂乱无序的代码突然变得清晰明了起来,呵呵,其实不是代码杂乱,只是我原来的阅读思路比较混乱的缘故。
 读者可以抛开前面几篇文章,从这一篇开始往下读,希望能有所收获。
 源码分析类的文章,比较难写,小马哥毕竟没有候捷的春秋笔法(甚至连作文都写不好),能把一个深奥复杂的STL源码分析的如此透彻,所以只好尝试一些傻办法,这些傻办法包括:
在源码上直接加注释。但我只对重点的部分增加一些注释,细节的东西就不再深究,这样有助于写作的进度。
用①、②、③这样的序号来指引代码的阅读线索
用不同颜色标注出代码中值得注意的地方。
你有什么好的建议,欢迎提出来。
好,我们进入正题。

BT客户端的 main() 函数:
 C 和c++的可执行程序,通常都有一个 main() 函数,一切从这里开始。而 python 这种解释性语言,并没有 main() 函数的概念。你传递给解释器一个 .py 扩展名的python源码文件,解释器就会顺序去解释执行这个文件中的代码。所以,BT客户端的执行,是从最先被 python 解释器解释执行的那个文件开始的。这个文件应该是 btdownloadheadless.py,至于谁又来通知让 python 解释器执行这个文件的,我们以后再讨论。

【btdownloadheadless.py】(在BT源码的根目录下)
②def run(params):
    try:
        import curses
        curses.initscr()
        cols = curses.COLS
        # endwin() De-initialize the library, and return terminal to normal status
        curses.endwin()
    
    except:
        cols = 80
h = HeadlessDisplayer()
# 调用 download.py 中的 download 函数,我们的重点将转移到 download.py 文件,注意,该文件及以后要分析的代码都在 BT源码的 BitTorrent 子目录下。
③    download(params, h.chooseFile, h.display, h.finished, h.error, Event(), cols, h.newpath)
    if not h.done:
        h.failed()

 # 所有的 python的入门书籍中,都会告诉你下面这段代码的含义。这就是最先被解释执行的代码所在。
①if __name__ == '__main__':
run(argv[1:])


【download.py】
这个文件中,最主要的就是 download() 函数,客户端所有的一切都是从这里开始。这个函数代码比较多,我必须挑出其中最重要的代码,其它的部分,后续再分析。

④rawserver = RawServer(doneflag, config['timeout_check_interval'], config['timeout'], errorfunc
= errorfunc, maxconnects = config['max_allow_in'])

# 选择一个可用的端口作为监听端口
for listen_port in xrange(config['minport'], config['maxport'] + 1):
try:
     rawserver.bind(listen_port, config['bind'])
        break
except socketerror, e:
     pass
else:
errorfunc("Couldn't listen - " + str(e))
return

encoder = Encoder(connecter, rawserver,
myid, config['max_message_length'], rawserver.add_task,
config['keepalive_interval'], infohash, config['max_initiate'])

rawserver.listen_forever(encoder)

客 户端的核心类就是 RawServer,这个类我在“服务器端源码分析”(可在论坛中找到)文章中分析过它的作用,这里不再赘述。通过调用它的 listen_forever() 函数,BT 客户端就进入了一个循环之中。此后,所有的下载、上传、文件存储以及其它工作都在这一次次的循环之中完成。
注意到,在进入循环之前,调用了 RawServer::bind() 函数,BT客户端会选择一个可用的端口,然后通过监听这个端口,从而可以接受其它 peer 的连接请求(也就是BT对等连接)。这个端口可以称为“监听端口”。如果你有网络服务器的编程经验,从它的循环处理逻辑、监听端口的处理可以知道,这显然 就是一个典型的网络服务器的表现。所以,我在以前的文章中曾经说过,BT客户端同时也是一个服务器。
一旦在监听端口上有某个peer发来连接请 求,BT客户端会创建一个新的端口,这个端口用来与请求者建立连接;有几个peer请求连接,就会创建几个端口。我们可以说这是一些“被动端口”。在循环 的处理过程中,BT客户端还会根据情况,向其它 peer主动发出连接请求,每个连接也需要一个端口,这些端口可以称为“主动端口”。其实,这些都是网络编程中最基本的概念,不清楚的朋友还是首先要去看 看这方面的书籍。
这样,就有了一个“监听端口”,几个“被动端口”和几个“主动端口”,BT客户端通过 select() 函数来监视这些端口,一旦“监听端口”或者“被动端口”上有数据到来,或者有数据需要从“主动端口”上发送出去,那么select() 都可以及时感知并进行处理(其实是一个轮询的过程),这就称为“I/O多路复用”。如果“被动端口”上有数据到来,那么这是BT对等连接的协议数据,需要 按照BT对等协议进行分析,并根据分析结果进行相应处理,这个分析处理的工作就是由 Encoder 类来完成的。所以在 listen_forever() 函数中传递的参数就是一个 Encoder 类对象。关于对 Encoder 类的分析,以后分析到BT对等协议的处理的时候再说。

小结:
 通过这篇文章,我们了解了BT客户端是从哪里执行的,相应的源码在哪 里。同时我们知道了BT客户端是以一个服务器循环的形式运行的,它需要监听一个端口,用于接受其它peers的连接请求;在循环过程中,它通过 select 来实现“I/O多路复用”;并由 Encoder 类来完成BT对等协议的分析处理。

posted on 2007-01-19 00:22 苦笑枯 阅读(778) 评论(0)  编辑  收藏 所属分类: P2P

只有注册用户登录后才能发表评论。


网站导航:
 
收藏来自互联网,仅供学习。若有侵权,请与我联系!

<2007年1月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(2)

随笔分类(56)

随笔档案(56)

搜索

  •  

最新评论

阅读排行榜

评论排行榜