BT客戶端原始碼分析之八 BT對等連線的建立過程

2021-04-28 07:46:54 字數 3916 閱讀 3931

客戶端主動發起連線

【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...