客戶端主動發起連線
【encrypter.py】
上一節的最後,我們看到呼叫 encoder::start_connection() 來向其它 peer 發起連線。所以,從這裡開始看起。
客戶端被動接受外來連線
客戶端在與 tracker 通訊的時候,已經把自己的 ip 和監聽的 port 報告給了 tracker。這樣,其它 peers 就可以通過這個 ip 和 port 來連線它了。例如上圖中的c,就主動給 a 發乙個連線,從c的角度來說,它是「主動連線」,但從a的角度,它是「被動連線」。
一旦有外來連線請求,就會呼叫 rawserver::handle_events(),下面是摘錄的「tracker 伺服器原始碼分析之二:rawserver類」中的一段。
最後呼叫的是 handle 的 external_connection_made(),對客戶端來說,這個handle 是 encoder 類物件。所以,外來連線建立成功後,最後呼叫的是 encoder:: external_connection_made():
③def external_connection_made(self, connection):
# 同樣是建立乙個新的 connection 類,並加入 connections 字典中。但不同之處在於最後乙個引數是 false,表明這個連線是由外部發起的。
self.connections[connection] = connection(self, connection, none, false)
bt對等連線握手:第一步
如果是主動連線,那麼一旦連線建立成功之後,就給對方傳送乙個握手訊息,我們折回去看序號為 4 的**:
if self.locally_initiated:
connection.write(chr(len(protocol_name)) + protocol_name +
(chr(0) * 8) + self.encoder.download_id)
if self.id is not none:
connection.write(self.encoder.my_id)
它用來唯一標識乙個 peer。
握手過程已經完成了第一步,還需要第二步,接收到對方的握手訊息,握手過程才算完成。所以接下去看在有資料到來的時候,是如何處理的。
bt對等連線握手:第二步
當tcp連線上有資料到來的時候, rawserver 會呼叫到 encoder:: data_came_in()
【encoder】
⑤def data_came_in(self, connection, data):
self.connections[connection].data_came_in(data)
進一步呼叫 connection::data_came_in()
【connection】
⑥def data_came_in(self, s):
#這個迴圈處理用來對bt對等連線中的訊息進行分析
while true:
if self.closed:
return
i = self.next_len - self.buffer.tell()
if i > len(s):
self.buffer.write(s)
return
self.buffer.write(s[:i])
s = s[i:]
m = self.buffer.getvalue()
self.buffer.reset()
self.buffer.truncate()
try:
x = self.next_func(m) #呼叫訊息分析函式,第乙個被呼叫的是read_header_len
except:
self.next_len, self.next_func = 1, self.read_dead
raise
if x is none:
self.close()
return
self.next_len, self.next_func = x
⑧def read_header_len(self, s):
# 協議的長度是否為 19?
if ord(s) != len(protocol_name):
return none
return len(protocol_name), self.read_header # 下乙個處理函式
def read_header(self, s):
# 協議名稱是否是「bittorrent protocol」?
if s != protocol_name:
return none
return 8, self.read_reserved # 下乙個處理函式
def read_reserved(self, s):
#8個保留位元組
return 20, self.read_download_id # 下乙個處理函式
def read_download_id(self, s):
對方 download_id 和自己計算出的是否一致?
if s != self.encoder.download_id:
return none
檢查完 download_id,就認為對方已經通過檢查了。
這裡很關鍵!!!,需要仔細體會。這實際上就是在被動連線情況下,完成握手過程的處理。如果連線不是由本地發起的(被動接收到乙個握手訊息),那麼給對方回乙個握手訊息。這裡的握手訊息傳送處理和第一步是一樣的
if not self.locally_initiated:
self.connection.write(chr(len(protocol_name)) + protocol_name +
(chr(0) * 8) + self.encoder.download_id + self.encoder.my_id)
return 20, self.read_peer_id #下乙個處理函式
def read_peer_id(self, s):
# connection 類用來管理乙個 bt 對等連線。在握手完成之後,就用對方的 peer_id 來唯一標識這個 connection。這個值被儲存在 self.id 中。顯然,在握手完成之前,這個 id 還是空值。
if not self.id:
# 對方的peer_id 可千萬別跟自己的一樣
if s == self.encoder.my_id:
return none
唔,如果 peer_id 已經收到過,也不繼續下去了
for v in self.encoder.connections.values():
if s and v.id == s:
return none
用對方的 peer_id 為 self.id 賦值,唯一標識這個 connection
self.id = s
if self.locally_initiated:
self.connection.write(self.encoder.my_id)
else:
self.encoder.everinc = true
else:
if s != self.id:
return none
# ok,握手完成!!!
self.complete = true
self.encoder.connecter.connection_made(self)
return 4, self.read_len #下乙個處理函式。從此以後,就是對其它bt對等訊息的處理過程了。這是我們下一節分析的問題。
小結:這篇文章重點分析了bt客戶端主動發起連線和被動接收連線的過程,以及在這兩種情況下,如何進行bt對等握手的處理。
在bt對等握手完成之後,連線的雙方就可以互相傳送bt對等訊息了,這是下一節的內容。
BT客戶端原始碼分析之一 總述
概述 相對於 tracker 伺服器來說,bt客戶端要複雜的多,bram cohen 花了一年 full time 的時間來完成 bt,我估計其中大部分時間是用在 bt 客戶端的實現和除錯上了。由 於 bt 客戶端涉及的 比較多,我不能再象分析 tracker 伺服器那樣,走上來就深入到細節之中去,...
BT客戶端原始碼分析之一 總述
概述 相對於 tracker 伺服器來說,bt客戶端要複雜的多,bram cohen 花了一年 full time 的時間來完成 bt,我估計其中大部分時間是用在 bt 客戶端的實現和除錯上了。由 於 bt 客戶端涉及的 比較多,我不能再象分析 tracker 伺服器那樣,走上來就深入到細節之中去,...
客戶端提交mr job原始碼流程分析
job job.getinstance獲得job物件 job.set 新增configuration等配置引數 job.waitforcomplete 原始碼內部實則呼叫submit 方法 之後jobsubmiter中有個成員cluster cluster中又有個成員proxy 物件,幫助提交到ya...