日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[以太坊源代码分析] VI. 基于p2p的底层通信(上篇)

發(fā)布時(shí)間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [以太坊源代码分析] VI. 基于p2p的底层通信(上篇) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

以太坊作為一個(gè)去中心化的系統(tǒng),其底層個(gè)體相互間的通信顯然非常重要,所有數(shù)據(jù)的同步,各個(gè)個(gè)體狀態(tài)的更新,都依賴于整個(gè)網(wǎng)絡(luò)中每個(gè)個(gè)體相互間的通信機(jī)制。以太坊的網(wǎng)絡(luò)通信基于peer-to-peer(p2p)通信協(xié)議,又根據(jù)自身傳輸數(shù)據(jù)類型(區(qū)塊,交易,哈希值等),網(wǎng)絡(luò)節(jié)點(diǎn)業(yè)務(wù)相關(guān)性等需求,在各方面做了特別設(shè)計(jì)。

由于以太坊中p2p通信相關(guān)代碼量較大,打算分為上下兩篇文章來加以詳解:上篇主要介紹管理p2p通信的核心類ProtocolManager內(nèi)部主要流程,以及通信相關(guān)協(xié)議族的設(shè)計(jì);下篇主要介紹ProtocolManager的兩個(gè)成員Fetcher和Downloader,這里是上篇。

1. 一般意義上的p2p網(wǎng)絡(luò)

在開始介紹以太坊的p2p通信機(jī)制之前,不妨先來看看一般意義上的p2p網(wǎng)絡(luò)通信的一些特征,以下部分內(nèi)容摘自peer-to-peer_wiki

peer-to-peer(p2p)首先是一種網(wǎng)絡(luò)拓?fù)漕愋?#xff0c;與之對(duì)比最顯著的就是client/server(C/S)架構(gòu)。從TCP/IP協(xié)議族分層的角度來說,p2p網(wǎng)絡(luò)中實(shí)際的數(shù)據(jù)交換,依然是網(wǎng)絡(luò)層用IP協(xié)議,傳輸層用TCP協(xié)議;而p2p協(xié)議--如果可稱之為協(xié)議的話,應(yīng)算作應(yīng)用層再往上,類似于邏輯拓?fù)鋵?#xff0c;畢竟著名的應(yīng)用層協(xié)議之一FTP,就屬于非常典型的一種C/S架構(gòu)類型。

上圖是C/S架構(gòu)和p2p架構(gòu)的一個(gè)簡單示意圖,原圖來自wiki。左圖中C/S架構(gòu)被描繪成星型拓?fù)?#xff0c;這當(dāng)然僅僅是特例,大家可能在工作中遇到各種各樣拓?fù)湫螤畹腃/S架構(gòu),而其核心特征是不變的:C/S 網(wǎng)絡(luò)中的個(gè)體地位和功能是不平等的,client個(gè)體主要消耗資源,發(fā)起請(qǐng)求,server個(gè)體主要提供資源并處理請(qǐng)求,這使得C/S架構(gòu)天然是中心化的

相比之下,p2p架構(gòu)中最重要的特點(diǎn)在于:其網(wǎng)絡(luò)中的個(gè)體在地位和功能上是平等的,雖然每個(gè)個(gè)體可能處理不同的請(qǐng)求,實(shí)際提供的資源在具體量化后可能有差異,但它們都能同時(shí)既消耗資源又提供資源。如果把整個(gè)所處網(wǎng)絡(luò)中的資源--此處的資源包括但不限于運(yùn)算能力、存儲(chǔ)空間、網(wǎng)絡(luò)帶寬等,視為一個(gè)總量,那么p2p網(wǎng)絡(luò)中的資源分布,是分散于各個(gè)個(gè)體中的(也許不一定均勻分布)。所以,p2p網(wǎng)絡(luò)架構(gòu)天然是去中心化的、分布式的

注意上圖右側(cè)p2p網(wǎng)絡(luò)中,并非每個(gè)個(gè)體與網(wǎng)絡(luò)中其他同類均有通信。這其實(shí)也是p2p網(wǎng)絡(luò)的一個(gè)很重要的特點(diǎn):一個(gè)個(gè)體只需要與相鄰的一部分同類有通信即可,每個(gè)個(gè)體可與多少相鄰個(gè)體、哪些個(gè)體有通信,是可以加以設(shè)計(jì)的,

無結(jié)構(gòu)化的和有結(jié)構(gòu)化的p2p網(wǎng)絡(luò)

根據(jù)p2p網(wǎng)絡(luò)中節(jié)點(diǎn)相互之間如何聯(lián)系,可以將p2p網(wǎng)絡(luò)簡單區(qū)分為無結(jié)構(gòu)化的(unstructured),和結(jié)構(gòu)化的(structured)兩大類。

無結(jié)構(gòu)化的

這種p2p網(wǎng)絡(luò)即最普通的,不對(duì)結(jié)構(gòu)作特別設(shè)計(jì)的實(shí)現(xiàn)方案。優(yōu)點(diǎn)是結(jié)構(gòu)簡單易于組建,網(wǎng)絡(luò)局部區(qū)域內(nèi)個(gè)體可任意分布,反正此時(shí)網(wǎng)絡(luò)結(jié)構(gòu)對(duì)此也沒有限制;特別是在應(yīng)對(duì)大量新個(gè)體加入網(wǎng)絡(luò)和舊個(gè)體離開網(wǎng)絡(luò)(“churn”)時(shí)它的表現(xiàn)非常穩(wěn)定。缺點(diǎn)在于在該網(wǎng)絡(luò)中查找數(shù)據(jù)的效率太低,因?yàn)闆]有預(yù)知信息,所以往往需要將查詢請(qǐng)求發(fā)遍整個(gè)網(wǎng)絡(luò)(至少大多數(shù)個(gè)體),這會(huì)占用很大一部分網(wǎng)絡(luò)資源,并大大拖慢網(wǎng)絡(luò)中其他業(yè)務(wù)運(yùn)行。

結(jié)構(gòu)化的

這種p2p網(wǎng)絡(luò)中的個(gè)體分布經(jīng)過精心設(shè)計(jì),主要目的是為了提高查詢數(shù)據(jù)的效率,降低查詢數(shù)據(jù)帶來的資源消耗。提高查詢效率的基本手段是對(duì)數(shù)據(jù)建立索引,結(jié)構(gòu)化p2p網(wǎng)絡(luò)最普遍的實(shí)現(xiàn)方案中使用了分布式哈希表(Distributed Hash Table,DHT),它會(huì)對(duì)每項(xiàng)數(shù)據(jù)(value)分配一個(gè)key以組成(key,value)鍵值對(duì),同時(shí)網(wǎng)絡(luò)中每個(gè)個(gè)體的分布--這里的分布主要指相互通信關(guān)系-根據(jù)key鍵進(jìn)行關(guān)聯(lián)和擴(kuò)展。這樣,當(dāng)要查找某項(xiàng)數(shù)據(jù)時(shí),只要跟據(jù)其key鍵就能不斷的縮小查找區(qū)域,大大減少資源消耗。

盡管如此,這樣的p2p網(wǎng)絡(luò)缺點(diǎn)也很明顯:由于每個(gè)個(gè)體需要存有數(shù)量不少的相鄰個(gè)體列表,所以當(dāng)網(wǎng)絡(luò)中發(fā)生大量新舊個(gè)體頻繁加入和離開的“churn”事件時(shí),整個(gè)網(wǎng)絡(luò)的性能會(huì)大幅惡化,因?yàn)槊總€(gè)個(gè)體的很大一部分資源消耗在相鄰列表更新上(包括自身相鄰列表的更新,和相互之間更新所儲(chǔ)列表),同時(shí)許多peer所在的key也需要重新定義;另外,哈希表本身容量是有使用限制的,當(dāng)哈希表中存儲(chǔ)的數(shù)據(jù)空間大于其設(shè)計(jì)容量的一半時(shí),哈希表就會(huì)大概率出現(xiàn)“碰撞”事故,這樣的限制也使得依據(jù)DHT建立的p2p網(wǎng)絡(luò)的整體效率大打折扣。

對(duì)于以太坊通信機(jī)制的借鑒

根據(jù)以太坊的運(yùn)行特點(diǎn),我們可以大概勾勒出以太坊個(gè)體也就是客戶端所組成網(wǎng)絡(luò)的一些需求特征:

  • 網(wǎng)絡(luò)中隨時(shí)可能存在一些個(gè)體加入和離開網(wǎng)絡(luò)的情況,但同一時(shí)間內(nèi)大量新舊個(gè)體同時(shí)發(fā)生加入或離開的概率很低。
  • 每個(gè)個(gè)體所存儲(chǔ)的數(shù)據(jù)(區(qū)塊),理想狀態(tài)下是相同的。也許有些個(gè)體會(huì)存在更新不夠及時(shí),例如新挖掘區(qū)塊/新創(chuàng)建交易的廣播事件到達(dá)有延遲,或者有些個(gè)體需要在狀態(tài)更新后更換自己所維持區(qū)塊鏈中的區(qū)塊,但相應(yīng)的通信機(jī)制一定是希望將這些差異抹平的。所以在以太坊網(wǎng)絡(luò)中,查找數(shù)據(jù)時(shí)并不需要針對(duì)某些特定區(qū)域以提高效率,當(dāng)然也不需要向整個(gè)網(wǎng)絡(luò)大水漫灌的發(fā)送請(qǐng)求,正常情況下任意一個(gè)(或相鄰幾個(gè))個(gè)體就可以提供。
  • 綜上所述,我們對(duì)以太坊中的p2p網(wǎng)絡(luò)設(shè)計(jì)可以有個(gè)初步思路了:

    • 不需要結(jié)構(gòu)化,經(jīng)過改進(jìn)的非結(jié)構(gòu)化(比如設(shè)計(jì)好相鄰個(gè)體列表)網(wǎng)絡(luò)模型可以滿足需求;
    • 個(gè)體間的相互同步更新需要仔細(xì)設(shè)計(jì);

    之后的章節(jié)中,我們可以逐步了解以太坊中的這個(gè)p2p網(wǎng)絡(luò)通信是如何完善并實(shí)現(xiàn)的。

    2. p2p通信的管理模塊ProtocolManager

    以太坊中,管理個(gè)體間p2p通信的頂層結(jié)構(gòu)體叫eth.ProtocolManager,它也是eth.Ethereum的核心成員變量之一。先來看一下它的主要UML關(guān)系:


    ProtocolManager主要成員包括:

    • peertSet{}類型成員用來緩存相鄰個(gè)體列表,peer{}表示網(wǎng)絡(luò)中的一個(gè)遠(yuǎn)端個(gè)體。
    • 通過各種通道(chan)事件訂閱(subscription)的方式,接收和發(fā)送包括交易和區(qū)塊在內(nèi)的數(shù)據(jù)更新。當(dāng)然在應(yīng)用中,訂閱也往往利用通道來實(shí)現(xiàn)事件通知。
    • ProtocolManager用到的這些通道的另一端,可能是其他的個(gè)體peer,也可能是系統(tǒng)內(nèi)單例的數(shù)據(jù)源比如txPool,或者是事件訂閱的管理者比如event.Mux。
    • Fetcher類型成員累積所有其他個(gè)體發(fā)送來的有關(guān)新數(shù)據(jù)的宣布消息,并在自身對(duì)照后,安排相應(yīng)的獲取請(qǐng)求。
    • Downloader類型成員負(fù)責(zé)所有向相鄰個(gè)體主動(dòng)發(fā)起的同步流程。

    小小說明:這里提到的"遠(yuǎn)端"個(gè)體,即非本peer的其他peer對(duì)象。以太坊的p2p網(wǎng)絡(luò)中,所有進(jìn)行通信的兩個(gè)peer都必須率先經(jīng)過相互的注冊(cè)(register),并被添加到各自緩存的peer列表,也就是peerSet{}對(duì)象中,這樣的兩個(gè)peers,就可以稱為“相鄰”。所以,這里提到的“遠(yuǎn)端"個(gè)體,如果處于可通信狀態(tài),則必定已經(jīng)“相鄰”。

    在運(yùn)行方面,Start()函數(shù)是ProtocolManager的啟動(dòng)函數(shù),它會(huì)在eth.Ethereum.Start()中被主動(dòng)調(diào)用。ProtocolManager.Start()會(huì)啟用4個(gè)單獨(dú)線程(goroutine,協(xié)程)去分別執(zhí)行4個(gè)函數(shù),這也標(biāo)志著該以太坊個(gè)體p2p通信的全面啟動(dòng)。

    Start():全面啟動(dòng)p2p通信

    由Start()啟動(dòng)的四個(gè)函數(shù)在業(yè)務(wù)邏輯上各有側(cè)重,下圖是關(guān)于它們所在流程的簡單示意圖:


    以上這四段相對(duì)獨(dú)立的業(yè)務(wù)流程的邏輯分別是:

    • 廣播新出現(xiàn)的交易對(duì)象。txBroadcastLoop()會(huì)在txCh通道的收端持續(xù)等待,一旦接收到有關(guān)新交易的事件,會(huì)立即調(diào)用BroadcastTx()函數(shù)廣播給那些尚無該交易對(duì)象的相鄰個(gè)體。
    • 廣播新挖掘出的區(qū)塊。minedBroadcastLoop()持續(xù)等待本個(gè)體的新挖掘出區(qū)塊事件,然后立即廣播給需要的相鄰個(gè)體。當(dāng)不再訂閱新挖掘區(qū)塊事件時(shí),這個(gè)函數(shù)才會(huì)結(jié)束等待并返回。很有意思的是,在收到新挖掘出區(qū)塊事件后,minedBroadcastLoop()會(huì)連續(xù)調(diào)用兩次BroadcastBlock(),兩次調(diào)用僅僅一個(gè)bool型參數(shù)@propagate不一樣,當(dāng)該參數(shù)為true時(shí),會(huì)將整個(gè)新區(qū)塊依次發(fā)給相鄰區(qū)塊中的一小部分;而當(dāng)其為false時(shí),僅僅將新區(qū)塊的Hash值和Number發(fā)送給所有相鄰列表。
    • 定時(shí)與相鄰個(gè)體進(jìn)行區(qū)塊全鏈的強(qiáng)制同步。syncer()首先啟動(dòng)fetcher成員,然后進(jìn)入一個(gè)無限循環(huán),每次循環(huán)中都會(huì)向相鄰peer列表中“最優(yōu)”的那個(gè)peer作一次區(qū)塊全鏈同步。發(fā)起上述同步的理由分兩種:如果有新登記(加入)的相鄰個(gè)體,則在整個(gè)peer列表數(shù)目大于5時(shí),發(fā)起之;如果沒有新peer到達(dá),則以10s為間隔定時(shí)的發(fā)起之。這里所謂"最優(yōu)"指的是peer中所維護(hù)區(qū)塊鏈的TotalDifficulty(td)最高,由于Td是全鏈中從創(chuàng)世塊到最新頭塊的Difficulty值總和,所以Td值最高就意味著它的區(qū)塊鏈?zhǔn)亲钚碌?/span>,跟這樣的peer作區(qū)塊全鏈同步,顯然改動(dòng)量是最小的,此即"最優(yōu)"。
    • 將新出現(xiàn)的交易對(duì)象均勻的同步給相鄰個(gè)體。txsyncLoop()主體也是一個(gè)無限循環(huán),它的邏輯稍微復(fù)雜一些:首先有一個(gè)數(shù)據(jù)類型txsync{p, txs},包含peer和tx列表;通道txsyncCh用來接收txsync{}對(duì)象;txsyncLoop()每次循環(huán)時(shí),如果從通道txsyncCh中收到新數(shù)據(jù),則將它存入一個(gè)本地map[]結(jié)構(gòu),k為peer.ID,v為txsync{},并將這組tx對(duì)象發(fā)送給這個(gè)peer;每次向peer發(fā)送tx對(duì)象的上限數(shù)目100*1024,如果txsync{}對(duì)象中有剩余tx,則該txsync{}對(duì)象繼續(xù)存入map[]并更新tx數(shù)目;如果本次循環(huán)沒有新到達(dá)txsync{},則從map[]結(jié)構(gòu)中隨機(jī)找出一個(gè)txsync對(duì)象,將其中的tx組發(fā)送給相應(yīng)的peer,重復(fù)以上循環(huán)。

    以上四段流程就是ProtocolManager向相鄰peer主動(dòng)發(fā)起的通信過程。盡管上述各函數(shù)細(xì)節(jié)從文字閱讀起來容易模糊,不過最重要的內(nèi)容還是值得留意下的:本個(gè)體(peer)向其他peer主動(dòng)發(fā)起的通信中,按照數(shù)據(jù)類型可分兩類:交易tx和區(qū)塊block而按照通信方式劃分,亦可分為廣播新的單個(gè)數(shù)據(jù)和同步一組同類型數(shù)據(jù),這樣簡單的兩兩配對(duì),便可組成上述四段流程。


    上述函數(shù)的實(shí)現(xiàn)中,很多地方都體現(xiàn)出巧妙的設(shè)計(jì),比如BroadcastBlock()中,如果發(fā)送區(qū)塊block,由于數(shù)據(jù)量相對(duì)重量級(jí),則僅僅選擇一小部分相鄰peer,而如果發(fā)送hash值 + Number值,則發(fā)給所有相鄰peer;又比如txsyncLoop()中,會(huì)從map[]中隨機(jī)選擇一個(gè)peer進(jìn)行發(fā)送(隨機(jī)選擇的txsync{}中包含peer)。這些細(xì)節(jié),很好的控制了單次業(yè)務(wù)請(qǐng)求的資源消耗對(duì)于定向區(qū)域的傾向性,使得整個(gè)網(wǎng)絡(luò)資源消耗愈加均衡,體現(xiàn)出非常全面的設(shè)計(jì)思路。

    handle():交給其他peer的回調(diào)函數(shù)

    對(duì)于peer間通信而言,除了己方需要主動(dòng)向?qū)Ψ絧eer發(fā)起通信(比如Start()中啟動(dòng)的四個(gè)獨(dú)立流程)之外,還需要一種由對(duì)方peer主動(dòng)調(diào)用的數(shù)據(jù)傳輸,這種傳輸不僅僅是由對(duì)方peer發(fā)給己方,更多的用法是對(duì)方peer主動(dòng)調(diào)用一個(gè)函數(shù)讓己方發(fā)給它們某些特定數(shù)據(jù)。這種通信方式,在代碼實(shí)現(xiàn)上適合用回調(diào)(callback)來實(shí)現(xiàn)。

    ProtocolManager.handle()就是這樣一個(gè)函數(shù),它會(huì)在ProtocolManager對(duì)象創(chuàng)建時(shí),以回調(diào)函數(shù)的方式“埋入”每個(gè)p2p.Protocol對(duì)象中(實(shí)現(xiàn)了Protocol.Run()方法)。之后每當(dāng)有新peer要與己方建立通信時(shí),如果對(duì)方能夠支持該P(yáng)rotocol,那么雙方就可以順利的建立并開始通信。以下是handle()的基本代碼:

    [plain]?view plain?copy
  • //?/eth/handler.go??
  • func?(pm?*ProtocolManager)?handle(p?*peer)?error?{??
  • ????td,?head,?genesis?:=?pm.blockchain.Status()??
  • ????p.Handshake(pm.networkId,?td,?head,?genesis)??
  • ??
  • ????if?rw,?ok?:=?p.rw.(*meteredMsgReadWriter);?ok?{??
  • ????????rm.Init(p.version)??
  • ????}??
  • ??
  • ????pm.peers.Register(p)??
  • ????defer?pm.removePeer(p.id)??
  • ??
  • ????pm.downloader.RegisterPeer(p.id,?p.version,?p)??
  • ??
  • ????pm.syncTransactions(p)??
  • ????...??
  • ????for?{??
  • ????????if?err?:=?pm.handleMsg(p);?err?!=?nil?{??
  • ????????????return?err??
  • ????????}??
  • ????}??
  • }??
  • handle()函數(shù)針對(duì)一個(gè)新peer做了如下幾件事:

  • 握手,與對(duì)方peer溝通己方的區(qū)塊鏈狀態(tài)
  • 初始化一個(gè)讀寫通道,用以跟對(duì)方peer相互數(shù)據(jù)傳輸。
  • 注冊(cè)對(duì)方peer,存入己方peer列表;只有handle()函數(shù)退出時(shí),才會(huì)將這個(gè)peer移除出列表。
  • Downloader成員注冊(cè)這個(gè)新peer;Downloader會(huì)自己維護(hù)一個(gè)相鄰peer列表。
  • 調(diào)用syncTransactions(),用當(dāng)前txpool中新累計(jì)的tx對(duì)象組裝成一個(gè)txsync{}對(duì)象,推送到內(nèi)部通道txsyncCh。還記得Start()啟動(dòng)的四個(gè)函數(shù)么? 其中第四項(xiàng)txsyncLoop()中用以等待txsync{}數(shù)據(jù)的通道txsyncCh,正是在這里被推入txsync{}的。
  • 在無限循環(huán)中啟動(dòng)handleMsg(),當(dāng)對(duì)方peer發(fā)出任何msg時(shí),handleMsg()可以捕捉相應(yīng)類型的消息并在己方進(jìn)行處理。
  • 建立新peer連接和傳遞Protocol[]

    剛才提到,handle()函數(shù)以回調(diào)函數(shù)的形式被放入一個(gè)p2p.Protocol{}里,那么Protocol對(duì)象是如何交給新peer的呢?這部分細(xì)節(jié),隱藏在新peer連接建立的過程中。

    所有遠(yuǎn)端peer與己方之間的通信,都是通過p2p.Server{}來管理的,Server在整個(gè)客戶端最早的啟動(dòng)步驟Node.Start()中被創(chuàng)建并啟動(dòng),而node.Node是用來承載客戶端中所有node.<Service>實(shí)現(xiàn)體的容器,下圖簡單示意了Node.Start()中與Server相關(guān)的一些步驟:


    Node.Start()中首先會(huì)創(chuàng)建p2p.Server{},此時(shí)Server中的Protocol[]還是空的;然后將Node中載入的所有<Service>實(shí)現(xiàn)體中的Protocol都收集起來,一并交給Server對(duì)象,作為Server.Protocols列表;然后啟動(dòng)Server對(duì)象,并將Server對(duì)象作為參數(shù)去逐一啟動(dòng)每個(gè)<Service>實(shí)現(xiàn)體。

    而由于eth.Ethereum對(duì)于<Service>.Protocols()的實(shí)現(xiàn)中,正是搜集了ProtocolManager.Protocols而成,所以ProtocolManager.Protocols最終被導(dǎo)入了p2p.Server.Protocols.

    那么Server.Start()中做了什么呢? 下圖是Server.Start()和run()函數(shù)體內(nèi),與新peer創(chuàng)建相關(guān)的主要邏輯:


    可以看到,Server.Start()中啟動(dòng)一個(gè)單獨(dú)線程(listenLoop())去監(jiān)聽某個(gè)端口有無主動(dòng)發(fā)來的IP連接;另外一個(gè)單獨(dú)線程啟動(dòng)run()函數(shù),在無限循環(huán)里處理接收到的任何新消息新對(duì)象。在run()函數(shù)中,如果有遠(yuǎn)端peer發(fā)來連接請(qǐng)求(新的p2p.conn{}),則調(diào)用Server.newPeer()生成新的peer對(duì)象,并把Server.Protocols全交給peer。

    綜合這兩部分代碼邏輯,可以發(fā)現(xiàn):

  • ProtocolManager.Protocols 最終由Server賦予了每一個(gè)新連接上(新創(chuàng)建)的peer對(duì)象中,所以回調(diào)函數(shù)ProtocolManager.handle()也會(huì)進(jìn)入每一個(gè)新的遠(yuǎn)端peer對(duì)象中。而peer對(duì)象,需要接受目前客戶端Node中所有<Service>的Protocols列表。
  • 以太坊中每個(gè)peer接收新連接的過程,源于標(biāo)準(zhǔn)的TCP/IP監(jiān)聽來訪連接的方式(listener)。而每個(gè)新連接上的peer,都會(huì)由p2p.Server交給ProtocolManager。

  • 一點(diǎn)體會(huì)

    從上述邏輯流程中可以感受到,對(duì)于以太坊的p2p通信管理模塊來說,管理Protocol才是其最重要的任務(wù),尤其是通過Protocol中的回調(diào)函數(shù)的設(shè)定,可以在對(duì)方peer在發(fā)生任何事件時(shí),己方有足夠的邏輯進(jìn)行響應(yīng)。這也是這個(gè)核心結(jié)構(gòu)體為何被命名為ProtocolManager,而不是PeerManager的原因。至于管理peer群的功能,基本上用一個(gè)列表或者map結(jié)構(gòu),或者peerSet{}就夠了。

    3. p2p通信協(xié)議族的結(jié)構(gòu)設(shè)計(jì)

    在上文的介紹中,出現(xiàn)了多處有關(guān)p2p通信協(xié)議的結(jié)構(gòu)類型,比如eth.peer,p2p.Peer,Server等等。這里不妨對(duì)這些p2p通信協(xié)議族的結(jié)構(gòu)一并作個(gè)總解。以太坊中用到的p2p通信協(xié)議族的結(jié)構(gòu)類型,大致可分為三層:

    • 第一層處于pkg eth中,可以直接被eth.Ethereum,eth.ProtocolManager等頂層管理模塊使用,在類型聲明上也明顯考慮了eth.Ethereum的使用特點(diǎn)。典型的有eth.peer{}, eth.peerSet{},其中peerSet是peer的集合類型,而eth.peer代表了遠(yuǎn)端通信對(duì)象和其所有通信操作,它封裝更底層的p2p.Peer對(duì)象以及讀寫通道等。
    • 第二層屬于pkg p2p,可認(rèn)為是泛化的p2p通信結(jié)構(gòu),比較典型的結(jié)構(gòu)類型包括代表遠(yuǎn)端通信對(duì)象的p2p.Peer{}, 封裝自更底層連接對(duì)象的conn{},通信用通道對(duì)象protoRW{}, 以及啟動(dòng)監(jiān)聽、處理新加入連接或斷開連接的Server{}。這一層中,各種數(shù)據(jù)類型的界限比較清晰,盡量不出現(xiàn)揉雜的情況,這也是泛化結(jié)構(gòu)的需求。值得關(guān)注的是p2p.Protocol{},它應(yīng)該是針對(duì)上層應(yīng)用特意開辟的類型,主要作用包括容納應(yīng)用程序所要求的回調(diào)函數(shù)等,并通過p2p.Server{}在新連接建立后,將其傳遞給通信對(duì)象peer。從這個(gè)類型所起的作用來看,命名為Protocol還是比較貼切的,盡管不應(yīng)將其與TCP/IP協(xié)議等既有概念混淆。
    • 第三層處于golang自帶的網(wǎng)絡(luò)代碼包中,也可分為兩部分:第一部分pkg net,包括代表網(wǎng)絡(luò)連接的<Conn>接口,代表網(wǎng)絡(luò)地址的<Addr>以及它們的實(shí)現(xiàn)類;第二部分pkg syscall,包括更底層的網(wǎng)絡(luò)相關(guān)系統(tǒng)調(diào)用類等,可視為封裝了網(wǎng)絡(luò)層(IP)和傳輸層(TCP)協(xié)議的系統(tǒng)實(shí)現(xiàn)。

    下列UML圖描繪了上述三層p2p通信協(xié)議族中的一些主要結(jié)構(gòu),希望對(duì)于理解以太坊中p2p通信相關(guān)代碼有所幫助。



    小結(jié):

    諸如以太坊這種去中心化的數(shù)字貨幣運(yùn)行系統(tǒng),天生適用p2p通信架構(gòu)。不過原理雖然簡單,在系統(tǒng)架構(gòu)的層面,依然有很多實(shí)現(xiàn)細(xì)節(jié)需要加以關(guān)注。

  • eth.ProtocolManager中,會(huì)對(duì)每一個(gè)遠(yuǎn)端peer發(fā)起主動(dòng)傳輸數(shù)據(jù)的操作,這組操作按照數(shù)據(jù)類型區(qū)分,可分為交易和區(qū)塊;而若以發(fā)送數(shù)據(jù)方式來區(qū)分,亦可分為廣播單項(xiàng)數(shù)據(jù)和同步一組同類型數(shù)據(jù)。這樣兩兩配對(duì),即可形成4組主動(dòng)傳輸數(shù)據(jù)的操作。
  • ProtocolManager通過在p2p.Protocol{}對(duì)象中埋入回調(diào)函數(shù),可以對(duì)遠(yuǎn)端peer的任何事件及狀態(tài)更新作出響應(yīng)。這些Protocol對(duì)象,會(huì)由p2p.Server傳遞給每一個(gè)新連接上的遠(yuǎn)端peer。
  • 以太坊目前實(shí)現(xiàn)的p2p通信協(xié)議族的結(jié)構(gòu)類型中,按照功能和作用,可分為三層:頂層pkg eth中的類型直接服務(wù)于當(dāng)前以太坊系統(tǒng)(Ethereum,ProtocolManager等模塊),中間層pkg p2p是泛化結(jié)構(gòu)類型,底層包括golang語言包自帶的pkg net, syscall等,封裝了網(wǎng)絡(luò)層和傳輸層協(xié)議的系統(tǒng)實(shí)現(xiàn)。
  • 原文:http://blog.csdn.net/teaspring/article/details/78455046

    總結(jié)

    以上是生活随笔為你收集整理的[以太坊源代码分析] VI. 基于p2p的底层通信(上篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。