由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還支持異步發送數據