posts - 56,  comments - 12,  trackbacks - 0

这篇文章,我们来分析 RawServer 以及一些相关的类。 RawServer 类的实现代码,在 BitTorrent 子目录的 RawServer.py

 

RawServer 这个类的作用是实现一个网络服务器。关于网络编程的知识,《 unix 网络编程:卷 1 》是最经典的书籍,你如果对这块不了解,建议抽时间看看这本书。 RawServer 实现的是一种事件多路复用、非阻塞的网络模型。它使用的是 poll() (而不是我们常见的 select() ,关于 poll select 的比较,也在《 unix 网络编程:卷 1 》中有介绍)函数,处理过程大致是这样的:

首先创建一个监听 socket ,然后将这个 socket 加入 poll 的事件源;

随后进入服务处理循环,即:

 

调用 poll() 函数,这个函数会阻塞,直到网络上有某些事件发生或者超时才返回给调用者;

poll() 返回之后,先检查一下是否有没有处理的任务,如果有,那么先完成这些任务。然后根据事件类型进行处理。

如果是连接请求(监听 socket 上的 POLLIN 事件)到来,它 accept 这个请求,如果 accept 成功,那么就和一个 client 建立了连接,于是将 accept() 新创建的 socket 加入 poll 的事件源;

如果在已经建立的连接上(连接 socket 上的 POLLIN 事件),有数据可读,那么将数据从 client 端读过来,做进一步处理;

如果已经建立的连接已经准备好(连接 socket 上的 POLLOUT 事件),可以发送数据,则检查是否有数据需要发送,如果有,那么发送数据给 client 端。

 

(所以, tracker 是一个单进程的服务器,并没有用到线程。)

 

Bram Cohen 认为软件的可维护性非常重要,使代码易于维护的重要一条就是设计可重用的类, RawServer 在设计的时候,充分考虑到了可重用性,集中表现在两个地方:

1、  将网络 I/O 和数据分析处理分离。

网络服务器的事件多路复用、网络 I/O 部分通常是固定不变的,而数据在读取之后,进行分析处理的过程则是可变的。 RawServer 将可变的数据处理工作,交给另外一个抽象的类 Handler (实际上并没有这么一个类)来处理。比如,在 tracker 服务器的实现中,具体使用的就是 HTTPHandler 类,而在 以后将要分析的 BT client 实现代码中,用到的具体的 Handler Encoder 类。

 

2、  采用任务队列来抽象出任务处理的过程。

RawServer 维护了一个任务队列 unscheduled_tasks (实际是一个二元组的 list ,二元组的第一项是一个函数,第二项是超时时间)。在初始化的时候,首先向这个队列中加入一个任务: scan_for_timeouts() ,这样,每隔一段时间,服务器就会去检查一下是否有连接超时。如果有其它

 

RawServer 的成员函数中,对外暴露的有:

 

u       __init__ :(初始化函数)

 

u       add_task()

       在任务列表中增加一项任务(一个任务是一个函数以及一个指定的超时时间的组合)

 

u       bind()

       首先创建一个 socket ,然后设置 socket 的属性: SO_REUSEADDR IP_TOS, ,这两个属性的具体含义请参考《 unix 网络编程:卷 1 》,另外还将 socket 设置为非阻塞的。相对于阻塞的 socket 来说,非阻塞的 socket 在网络 I/O 性能上要提高许多,但是与此同时,编程的复杂度也要提高一些。象 tracker 这种可能同时要处理成千上万个并发连接的服务器,只能采用非阻塞的 socket

       然后将该 socket 和指定 ip 已经端口绑定;

       最后把这个 socket 加入 poll 的事件源。

 

u       start_connection()

       对外主动建立一个连接,这个函数在处理 NAT 穿越的时候用到了,我们后面分析到 NAT 穿越的时候,再具体讲解。

 

u       listen_forever()

       这个函数的功能就是实现了我在前面描述的网络服务器的处理过程。我们看到,它唯一的参数是 handler handler 的作用就是封装了对数据的具体处理。

listen_forever() 把对网络事件的处理过程,交给了 handle_events()

 

其它函数,包括 handle_events() ,都是内部函数(也就是外部不会直接来调用这些函数)。 Python 没有 c++ 那样 public protected private 这样的保护机制, python 类的内部函数命名的惯例是以下划线开始,例如 RawServer 中的 _close_dead() 等。

 

u       handle_events()

事件处理过程,主要是根据三种不同的网络事件分别处理,一是连接事件,二是读事件、三是写事件。

 

if sock == self.server.fileno()

 

这段代码判断发生事件的 socket 是否是监听 socket ,如果是,那么说明是连接事件。

连接事件的处理:

通过 accept 来接受连接,并将新建立的 socket 设置为非阻塞。

判断当前连接数是否已经达到了最大值(为了限制并发连接的数目,在初始化 RawServer 的时候,需要指定最大连接数目),如果已经达到最大值,那么关闭这个新建的连接。

否则,根据新的 socket 创建一个 SingleSocket 对象,( SingleSocket 封装了对 socket 的操作。)将这个对象加入内部的列表 single_sockets 中,以备后用。

将这个新 socket 加入 poll 的事件源

最后,调用 Handler external_connection_made() 函数,关于这个函数,在后面分析 HTTPHandler 时再讨论。

 

if (event & POLLIN) != 0:

这段代码判断是否是读事件

读事件的处理:

首先刷新一下连接的最后更新时间 last_hit )。

然后读取数据;

如果什么也没读到,那么说明连接被关闭了(在网络编程中,如果一个连接正常的被关闭,那么,也会触发读事件,只不过什么也读不到)

否则,调用 Handler data_came_in() 函数来处理读到的数据。

 

if (event & POLLOUT) != 0 and s.socket is not None and not s.is_flushed():

这段代码判断是否是写事件,而且确实有数据需要发送。在一个连接可以写的时候,就会发生写事件。

写事件的处理:

实际代码是在 SingleSocket try_write() 函数中。

在一个非阻塞的连接上发送指定大小的数据,很可能在一次发送过程中,数据没有被完全发送出去(只发送了一部分)就返回了,所以,每次 write 之后,必须判断是否完全发送了数据。如果没有发送完,那么下次有读事件的时候,还得回来继续发送未完得数据。这也是这个函数叫做 try_write 的原因吧。

try_write() 在最后,要重新设置 poll 的事件源。如果数据全部发送完毕了,那么只需要监听读事件( POLLIN )否则,既要监听读事件,也要监听写事件( POLLOUT ),这样,一旦连接变的可写,可以继续将剩下的数据发送出去。

 

u       scan_for_timeouts()

任务处理函数,它首先把自身加入未处理任务队列中,这样,经过一段时间,可以保证这个函数再次被调用,从而达到周期性调用的效果。

它检查每个连接是否超过指定时间没有被刷新,如果是,则该连接可能已经僵死,那么它关闭这个连接。

 

u       pop_unscheduled()

从任务列表中弹出一个未处理的任务。

 

 

RawServer 配合使用的是 SingleSocket 类,这是一个辅助类,主要目的是封装对 socket 的处理吧。包括数据的发送,都交给它来处理了。这个类比较简单,大家可以自己去看,我就不罗嗦了。

 

 

以上是对 RasServer 的具体实现的一个分析,可能读者看的还是晕晕糊糊,没办法,还是必须自己去看源代码,然后在遇到问题的时候,回头再来看这篇文章,才会有帮助。如果不亲自看源码,终究是纸上谈兵。

 

我们再来小结一下。

RawServer 封装了网络服务器的实现细节,它实现了一种事件多路处理、非阻塞的网络模型。它主要负责建立新的连接,从网络读取和发送数据,而对读到的数据的具体处理工作,交给 Handler 类来处理,从而把网络 I/O 和数据处理分离开来,使得 RawServer 可以重用。 Handler 类是在调用 listen_forever() 的时候,由调用者传递进来的,具体到 tracker 服务器,就是 HTTPHandler 。有了 RawServer tracker 就可以作为一个网络服务器运行了。

下一节,我们开始分析具体实现 tracker HTTP 协议处理的 HTTPHandler 类和 Tracker 类。

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

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


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

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

常用链接

留言簿(2)

随笔分类(56)

随笔档案(56)

搜索

  •  

最新评论

阅读排行榜

评论排行榜