本篇文章分析
HTTPHandler
類,它在
HTTPHandler.py
文件中。
上一篇我們講到,
RawServer
只負(fù)責(zé)網(wǎng)絡(luò)
I/O
,也就是從網(wǎng)絡(luò)上讀取和發(fā)送數(shù)據(jù),至于讀到的數(shù)據(jù)如何分析,以及應(yīng)該發(fā)送什么樣的數(shù)據(jù),則交給
Handler
類來處理。如果是用
c++
來實(shí)現(xiàn)的話,那么
Handler
應(yīng)該是一個接口類(提供幾個虛函數(shù)作為接口),但是
python
動態(tài)語言的特性,并不需要專門定義這么一個接口類,所以實(shí)際上并沒有
Handler
這么一個類。任何一個提供了以下成員函數(shù)的類,都可以作為一個
Handler
類來與
RawServer
配合,它們是:
external_connection_made()
:在建立新的連接的時(shí)候被調(diào)用
data_came_in()
:連接上有數(shù)據(jù)可讀的時(shí)候被調(diào)用
connection_flushed()
:當(dāng)在某個連接上發(fā)送完數(shù)據(jù)之后被調(diào)用
HTTPHandler
就是這樣一個
Handler
類,它具備以上接口。
HTTPHandler
代碼很少,因?yàn)樗阎饕ぷ饔纸唤o
HTTPConnection
了。
我們看
HTTPHandler
類的這幾個函數(shù):
l
external_connection_made()
:
每當(dāng)新來一個連接的時(shí)候,就創(chuàng)建一個
HTTPConnection
類。
l
data_came_in()
:
當(dāng)連接上有數(shù)據(jù)可讀的時(shí)候,調(diào)用
HTTPConnection::data_came_in()
。我們接下去看
HTTPConnection::data_came_in()
。
我們知道,
BT client
端與
tracker
服務(wù)器之間是通過
tracke HTTP
協(xié)議來進(jìn)行通信的。
HTTP
協(xié)議分為請求(
request
)和響應(yīng)(
response
),具體的協(xié)議請看相關(guān)的
RFC
文檔。我這里簡單講一下。
對
tracke
服務(wù)器來說,它讀到的數(shù)據(jù)是
client
端的
HTTP
請求。
HTTP
請求以行為單位,行的結(jié)束符是“回車換行”,也就是
ascii
字符
“
\r
”和“
\n
”。
第一行是請求的
URL
,例如:
GET /announce?ip=aaaaa;port=bbbbbbb HTTP/1.0
這行數(shù)據(jù)被空格分為三部分,
第一部分
GET
表示命令,其它命令還有
POST
、
HEAD
等等,常用的就是
GET
了。
第二部分是請求的
URL
,這里是
/announce?ip=aaaaa;port=bbbbbbb
。如果是普通的上網(wǎng)瀏覽網(wǎng)頁,那么
URL
就是我們要看的網(wǎng)頁在該
web
服務(wù)器上的相對路徑。但是,這里的
URL
僅僅是交互信息的一種方式,
client
端把要報(bào)告給
tracker
的信息,放在
URL
中,例子里面是
ip
和
port
,更詳細(xì)的信息請看“
BT
協(xié)議規(guī)范”中
tracker
協(xié)議部分。
第三部分是
HTTP
協(xié)議的版本號,在程序中忽略。
接下來的每一行,都是
HTTP
協(xié)議的消息頭部分,例如:
Host:www.sina.com.cn
Accept-encoding:gzip
通過消息頭,
tracker
服務(wù)器可以知道
client
端的一些信息,這其中比較重要的就是
Accept-encoding
,如果是
gzip
,那么說明
client
可以對
gzip
格式的數(shù)據(jù)進(jìn)行解壓,那么
tracker
服務(wù)器就可以考慮用
gzip
把響應(yīng)數(shù)據(jù)壓縮之后再傳回去,以減少網(wǎng)絡(luò)流量。我們可以在代碼中看到相應(yīng)的處理。
在消息頭的最后,是一個空行,表示消息頭結(jié)束了。對
GET
和
HEAD
命令來說,消息頭的結(jié)束,也就意味著整個
client
端的請求結(jié)束了。而對
POST
命令來說,可能后面還跟著其它數(shù)據(jù)。由于我們的
tracker
服務(wù)器只接受
GET
和
HEAD
命令,所以在協(xié)議處理過程中,如果遇到空行,那么就表示處理結(jié)束。
HTTPConnection::data_came_in()
用一個循環(huán)來進(jìn)行協(xié)議分析:
首先是尋找行結(jié)束符號:
i = self.buf.index('\n')
(我認(rèn)為僅僅找
“
\n
”并不嚴(yán)謹(jǐn),應(yīng)該找
“
\r\n
”這個序列)。
如果沒有找到,那么
index()
函數(shù)會拋出一個異常,而異常的處理是返回
True
,表示數(shù)據(jù)不夠,需要繼續(xù)讀數(shù)據(jù)。
如果找到了,那么
i
之前的字符串就是完整的一行。于是調(diào)用協(xié)議處理函數(shù),代碼是:
self.next_func = self.next_func(val)
在
HTTPConnection
的初始化的時(shí)候,有這么一行代碼:
self.next_func = self.read_type
next_func
是用來保存協(xié)議處理函數(shù)的,所以,第一個被調(diào)用的協(xié)議處理函數(shù)就是
read_type()
。它用來分析
client
端請求的第一行。在
read_type()
的最后,我們看到:
return self.read_header
這樣,在下一次調(diào)用
next_func
的時(shí)候,就是調(diào)用
read_header()
了,也就是對
HTTP
協(xié)議的消息頭進(jìn)行分析。
下面先看
read_type()
,
它首先把
GET
命令中的
URL
部分保存到
self.path
中,因?yàn)檫@是
client
端最關(guān)鍵的信息,后面要用到。
然后檢查一下是否是
GET
或者
HEAD
命令,如果不是,那么說明數(shù)據(jù)有錯誤。返回
None
,否則
return self.read_header
接下來我們看
read_header()
,
這其中,最重要的就是對空行的處理,因?yàn)榍懊嬲f了,空行表示協(xié)議分析結(jié)束。
在檢查完
client
端是否支持
gzip
編碼之后,調(diào)用:
r = self.handler.getfunc(self, self.path, self.headers)
通過一層層往后追查,發(fā)現(xiàn)
getfunc()
實(shí)際是
Tracker::get()
,也就是說,真正對
client
端發(fā)來的請求進(jìn)行分析,以及決定如何響應(yīng),是由
Tracker
來決定的。是的,這個
Tracker
在我們
tracker
服務(wù)器源碼分析系列的第一篇文章中就已經(jīng)看到了。在創(chuàng)建
RawServer
之后,馬上就創(chuàng)建了一個
Tracker
對象。所以,要了解
tracker
服務(wù)器到底是如何工作的,需要我們深入進(jìn)去分析
Tracker
類,那就是我們下一篇文章的工作了。
在調(diào)用完
Tracker::get()
之后,返回的是決定響應(yīng)給
client
端的數(shù)據(jù),
if r is not None:
self.answer(r)
最后,調(diào)用
answer()
來把這些數(shù)據(jù)發(fā)送給
client
端。
對
answer()
的分析,我們在下一篇分析
Tracker
類的文章中一并講解。
l
connection_flushed()
:
tracker
服務(wù)器用的是非阻塞的網(wǎng)絡(luò)
I/O
,所以不能保證在一次發(fā)送數(shù)據(jù)的操作中,把要發(fā)送的數(shù)據(jù)全部發(fā)送出去。
這個函數(shù),檢查在某個連接上需要發(fā)送的數(shù)據(jù),是否已經(jīng)全部被發(fā)送出去了,如果是的話,那么關(guān)閉這個連接的發(fā)送端。(為什么僅僅關(guān)閉發(fā)送端,而不是完全關(guān)閉這個連接了?疑惑)。
posted on 2007-01-19 00:18
苦笑枯 閱讀(386)
評論(0) 編輯 收藏 所屬分類:
P2P