以太坊peer之間連接建立的過程(源碼解析)

由newTasks產生的探測新節點的任務,並由這裏的start.Tasks開始進行嘗試連接

start.Tasks開始進行嘗試連接的調用棧(在dialTask.dial在完成網絡層三次握手):

連接成功以後,進行業務層握手(rlpx加密握手,協議握手,添加p2p.Peer節點對象,這個非常重要,承接所有節點數據傳輸)

新建p2p層Peer對象,並啓用這個對象

runPeer函數的具體實現:

啓動p2p網絡層下的Peer,這個對象的調用棧如下:

startProtocols的具體實現:

調用到NewProtocolManager裏註冊的eth協議的run方法:

// handle is the callback invoked to manage the life cycle of an eth peer. When

// this function terminates, the peer is disconnected.

func (pm *ProtocolManager) handle(p *peer) error {

// Ignore maxPeers if this is a trusted peer

if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted {

       return p2p.DiscTooManyPeers

}

p.Log().Debug("Ethereum peer connected", "name", p.Name())

// Execute the Ethereum handshake

var (

      genesis = pm.blockchain.Genesis()

      head    = pm.blockchain.CurrentHeader()

      hash    = head.Hash()

      number  = head.Number.Uint64()

      td      = pm.blockchain.GetTd(hash, number)

)

//交換彼此的節點信息(networkId,td,currentblockhash,genesishash

if err := p.Handshake(pm.networkId, td, hash, genesis.Hash()); err != nil {

      p.Log().Debug("Ethereum handshake failed", "err", err)

      return err

}

if rw, ok := p.rw.(*meteredMsgReadWriter); ok {

        rw.Init(p.version)

}

// Register the peer locally

//把這個peer保存在本地的peerSet中

if err := pm.peers.Register(p); err != nil {

      p.Log().Error("Ethereum peer registration failed", "err", err)

      return err

}

defer pm.removePeer(p.id)

// Register the peer in the downloader. If the downloader considers it banned, we disconnect

if err := pm.downloader.RegisterPeer(p.id, p.version, p); err != nil {

       return err

}

// Propagate existing transactions. new transactions appearing

// after this will be sent via broadcasts.

pm.syncTransactions(p)

// If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork

if daoBlock := pm.chainconfig.DAOForkBlock; daoBlock != nil {

      // Request the peer's DAO fork header for extra-data validation

      if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil {

      return err

}

// Start a timer to disconnect if the peer doesn't reply in time

p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {

       p.Log().Debug("Timed out DAO fork-check, dropping")

       pm.removePeer(p.id)

})

// Make sure it's cleaned up if the peer dies off

defer func() {

    if p.forkDrop != nil {

           p.forkDrop.Stop()

           p.forkDrop = nil

    }

}()

}

// main loop. handle incoming messages.

for {

     if err := pm.handleMsg(p); err != nil {

           p.Log().Debug("Ethereum message handling failed", "err", err)

            return err

    }

}

}

通過peer.Handshake交換了彼此的節點信息(td,currentblock),然後把這個peer保存在本地的peerSet中,以實現:

1.批量發送指定block給所有沒有此block的節點

2.批量發送指定tx給所有沒有此tx的節點

3.選取最佳節點(td最大),進行同步

看看peer的定義:
type peer struct {

id string                //peer id,通過p2p.Peer.id[0:7]獲得

*p2p.Peer

rw p2p.MsgReadWriter

version  int         // Protocol version negotiated

forkDrop *time.Timer // Timed connection dropper if forks aren't validated in time

head common.Hash        //對端的高度最大區塊的hash

td   *big.Int              //對端的目前的td(總難度)

lock sync.RWMutex

knownTxs    *set.Set                  // 緩存所有對端知道的tx(其就是發過的,或者從對端接收過的),防止重發

knownBlocks *set.Set                  // 緩存所有對端知道的block(其就是發過的,或者從對端接收過的),防止重發

queuedTxs   chan []*types.Transaction // 隊列緩存要發送給對端的所有txs

queuedProps chan *propEvent           // 隊列緩存要發送給對端的所有block

queuedAnns  chan *types.Block         // 隊列緩存要發送給對端的所有blockhash(主要是新區塊的宣告)

term        chan struct{}             // Termination channel to stop the broadcaster

}

看看peer的方法列表(同步節點信息,發送/去取區塊頭和區塊體,發送/去取state信息等):

peer還支持異步發送數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章