posts - 56,  comments - 12,  trackbacks - 0

本篇文章分析 HTTPHandler 类,它在 HTTPHandler.py 文件中。

上一篇我们讲到, RawServer 只负责网络 I/O ,也就是从网络上读取和发送数据,至于读到的数据如何分析,以及应该发送什么样的数据,则交给 Handler 类来处理。如果是用 c++ 来实现的话,那么 Handler 应该是一个接口类(提供几个虚函数作为接口),但是 python 动态语言的特性,并不需要专门定义这么一个接口类,所以实际上并没有 Handler 这么一个类。任何一个提供了以下成员函数的类,都可以作为一个 Handler 类来与 RawServer 配合,它们是:

 

external_connection_made() :在建立新的连接的时候被调用

data_came_in() :连接上有数据可读的时候被调用

connection_flushed() :当在某个连接上发送完数据之后被调用

 

       HTTPHandler 就是这样一个 Handler 类,它具备以上接口。

       HTTPHandler 代码很少,因为它把主要工作又交给 HTTPConnection 了。

       我们看 HTTPHandler 类的这几个函数:

 

l         external_connection_made()

每当新来一个连接的时候,就创建一个 HTTPConnection 类。

 

l         data_came_in()

当连接上有数据可读的时候,调用 HTTPConnection::data_came_in() 。我们接下去看 HTTPConnection::data_came_in()

 

我们知道, BT client 端与 tracker 服务器之间是通过 tracke HTTP 协议来进行通信的。 HTTP 协议分为请求( request )和响应( response ),具体的协议请看相关的 RFC 文档。我这里简单讲一下。

tracke 服务器来说,它读到的数据是 client 端的 HTTP 请求。

 

HTTP 请求以行为单位,行的结束符是“回车换行”,也就是 ascii 字符 \r ”和“ \n ”。

 

第一行是请求的 URL ,例如:

GET              /announce?ip=aaaaa;port=bbbbbbb       HTTP/1.0

 

这行数据被空格分为三部分,

第一部分 GET 表示命令,其它命令还有 POST HEAD 等等,常用的就是 GET 了。

第二部分是请求的 URL ,这里是 /announce?ip=aaaaa;port=bbbbbbb 。如果是普通的上网浏览网页,那么 URL 就是我们要看的网页在该 web 服务器上的相对路径。但是,这里的 URL 仅仅是交互信息的一种方式, client 端把要报告给 tracker 的信息,放在 URL 中,例子里面是 ip port ,更详细的信息请看“ BT 协议规范”中 tracker 协议部分。

第三部分是 HTTP 协议的版本号,在程序中忽略。

 

接下来的每一行,都是 HTTP 协议的消息头部分,例如:

Host:www.sina.com.cn

Accept-encoding:gzip

 

通过消息头, tracker 服务器可以知道 client 端的一些信息,这其中比较重要的就是 Accept-encoding ,如果是 gzip ,那么说明 client 可以对 gzip 格式的数据进行解压,那么 tracker 服务器就可以考虑用 gzip 把响应数据压缩之后再传回去,以减少网络流量。我们可以在代码中看到相应的处理。

在消息头的最后,是一个空行,表示消息头结束了。对 GET HEAD 命令来说,消息头的结束,也就意味着整个 client 端的请求结束了。而对 POST 命令来说,可能后面还跟着其它数据。由于我们的 tracker 服务器只接受 GET HEAD 命令,所以在协议处理过程中,如果遇到空行,那么就表示处理结束。

 

 

HTTPConnection::data_came_in() 用一个循环来进行协议分析:

首先是寻找行结束符号:

 

i = self.buf.index('\n')

 

(我认为仅仅找 \n ”并不严谨,应该找 \r\n ”这个序列)。

如果没有找到,那么 index() 函数会抛出一个异常,而异常的处理是返回 True ,表示数据不够,需要继续读数据。

如果找到了,那么 i   之前的字符串就是完整的一行。于是调用协议处理函数,代码是:

 

self.next_func = self.next_func(val)

 

HTTPConnection 的初始化的时候,有这么一行代码:

 

self.next_func = self.read_type

 

next_func 是用来保存协议处理函数的,所以,第一个被调用的协议处理函数就是 read_type() 。它用来分析 client 端请求的第一行。在 read_type() 的最后,我们看到:

return self.read_header

 

这样,在下一次调用 next_func 的时候,就是调用 read_header() 了,也就是对 HTTP 协议的消息头进行分析。

 

下面先看 read_type()

它首先把 GET 命令中的 URL 部分保存到 self.path 中,因为这是 client 端最关键的信息,后面要用到。

然后检查一下是否是 GET 或者 HEAD 命令,如果不是,那么说明数据有错误。返回 None ,否则 return self.read_header

 

接下来我们看 read_header()

这其中,最重要的就是对空行的处理,因为前面说了,空行表示协议分析结束。

在检查完 client 端是否支持 gzip 编码之后,调用:

 

r = self.handler.getfunc(self, self.path, self.headers)

 

通过一层层往后追查,发现 getfunc() 实际是 Tracker::get() ,也就是说,真正对 client 端发来的请求进行分析,以及决定如何响应,是由 Tracker 来决定的。是的,这个 Tracker 在我们 tracker 服务器源码分析系列的第一篇文章中就已经看到了。在创建 RawServer 之后,马上就创建了一个 Tracker 对象。所以,要了解 tracker 服务器到底是如何工作的,需要我们深入进去分析 Tracker 类,那就是我们下一篇文章的工作了。

 

在调用完 Tracker::get() 之后,返回的是决定响应给 client 端的数据,

if r is not None:

self.answer(r)

最后,调用 answer() 来把这些数据发送给 client 端。

 

answer() 的分析,我们在下一篇分析 Tracker 类的文章中一并讲解。

 

l         connection_flushed()

tracker 服务器用的是非阻塞的网络 I/O ,所以不能保证在一次发送数据的操作中,把要发送的数据全部发送出去。

这个函数,检查在某个连接上需要发送的数据,是否已经全部被发送出去了,如果是的话,那么关闭这个连接的发送端。(为什么仅仅关闭发送端,而不是完全关闭这个连接了?疑惑)。

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

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


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

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

常用链接

留言簿(2)

随笔分类(56)

随笔档案(56)

搜索

  •  

最新评论

阅读排行榜

评论排行榜