本篇文章分析
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
苦笑枯 阅读(381)
评论(0) 编辑 收藏 所属分类:
P2P