Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

深入区块链以太坊源码之p2p通信

Mohammad61417
126 0 0
一、p2p网络中分为有结构和无结构的网络
1 i0 y, f" g) y. s7 S9 v7 y无结构化的
7 o* P% n$ K5 x* b# _; w3 t这种p2p网络即最普通的,不对结构作特别设计的实现方案。
+ P. a4 N$ B  T1 T1 M+ O2 l- E优点是结构简单易于组建,网络局部区域内个体可任意分布,
! _( {3 T: I. j* R% [反正此时网络结构对此也没有限制;特别是在应对大量新个体加4 C- Z' Z0 z) |5 s3 ~
入网络和旧个体离开网络(“churn”)时它的表现非常稳定。
! X: v6 m/ J& f! q2 K缺点在于在该网络中查找数据的效率太低,因为没有预知信息,# J5 r) q$ h- ~, J
所以往往需要将查询请求发遍整个网络(至少大多数个体),
) e- i0 j; k* v这会占用很大一部分网络资源,并大大拖慢网络中其他业务运行。
& \, l: B8 ]* A$ {结构化的:
. a2 j3 o( u9 g; E这种p2p网络中的个体分布经过精心设计,主要目的是为了提高查询数据的效率,
+ H. g. ^6 a$ G# Y7 X5 j8 a& M降低查询数据带来的资源消耗。
8 n  G/ N8 u/ T. m以太坊采用了不需要结构化的结构,经过改进的非结构化(比如设计好相邻个体列表peerSet结构)
. r  k4 f4 Y1 O; U  N网络模型可以满足需求;  `. _5 ?3 S9 u
二、分布式hash表(DHT)
( }# ]5 @8 t, L7 @; z/ K; G" B保存数据
% K" J* {) D* e$ p' R2 d9 |(以下只是大致原理,具体的协议实现可能会有差异)
: O% `+ h: \9 W: F: r0 T当某个节点得到了新加入的数据(K/V),它会先计算自己与新数据的 key 之间的“距离”;; J4 W; a, v3 T1 J3 N
然后再计算它所知道的其它节点与这个 key 的距离。
; v# o/ M' N5 J$ P" o: I如果计算下来,自己与 key 的距离最小,那么这个数据就保持在自己这里。5 y9 ?- j0 E7 T' F
否则的话,把这个数据转发给距离最小的节点。4 w! q) r  \( X) |; p9 y
收到数据的另一个节点,也采用上述过程进行处理(递归处理)。! g8 K, V* ], j3 h" o$ B' ?
获取数据( |9 P1 W5 Q5 P3 b: J; O
(以下只是大致原理,具体的协议实现可能会有差异). p' x3 C: U: w* A, Z; j& {- @
当某个节点接收到查询数据的请求(key),它会先计算自己与 key 之间的“距离”;
7 |3 T& M& K3 I然后再计算它所知道的其它节点与这个 key 的距离。
  U1 m6 _& L. V( o; Z0 L$ U/ K- x如果计算下来,自己与 key 的距离最小,那么就在自己这里找有没有 key 对应的 value。
, M" z- w9 b) ^1 P' n6 B( X! i2 j有的话就返回 value,没有的话就报错。
. o( y1 L/ N0 [7 e1 L- U; }否则的话,把这个数据转发给距离最小的节点。
2 c, l2 V; ~7 ~; M& |5 D: M收到数据的另一个节点,也采用上述过程进行处理(递归处理)。! ^. a' ^$ o( Y  d+ l
三、以太坊中p2p通信的管理模块ProtocolManager
) d6 z5 p, [. ^4 o8 r/geth.go- G) \7 W, ^% g: i; h; O( o
// Start creates a live P2P node and starts running it.
+ z! P/ O9 p5 ~& v5 q2 d2 afunc (n *Node) Start() error {- Q4 M% T2 L# V; e4 \* B
   return n.node.Start()
: S/ U' x4 m; m1 i' I# A$ C}
' X8 d/ M9 U! f  `8 L/*) e( i& z+ ]' {4 i. B0 B, {5 N
   Protocol:容纳应用程序所要求的回调函数等.并通过p2p.Server{}在新连接建立后,将其传递给通信对象peer。
. y# Y6 G6 k6 W/ Z! t: y   Node.Start()中首先会创建p2p.Server{},此时Server中的Protocol[]还是空的;% M; o/ X5 x* i- M# y
   然后将Node中载入的所有实现体中的Protocol都收集起来,
( n* Q/ x8 ]# u$ V7 O   一并交给Server对象,作为Server.Protocols列表;然后启动Server对象,
9 }" H# Y& y" g6 T   并将Server对象作为参数去逐一启动每个实现体。, j& C8 v3 c8 g$ i- k9 o- t( G
*/% N  U# I' ~. P' P
/node.go
. X- ?, x6 G5 w" g// Start create a live P2P node and starts running it.
$ L, s0 z5 C! T2 z, [func (n *Node) Start() error {; }, b+ N3 D7 G7 Z
   ) b! O, U* V* `, D& |
   ...
( I2 w) ^8 e" h, Q4 d) G: Y! X   /*
' }. ^0 X6 |6 ^$ ?           ...
% E7 R9 {/ a; i8 E1 T/ f           初始化serverConfig' |8 [5 e* ^! r7 P" K1 |
   */# X: R, h8 x  U$ B7 M
   running := &p2p.Server{Config: n.serverConfig}
* W0 ]5 r7 Z% U0 F, @6 z" l1 ~1 U   ...
. k! o4 g. |, g6 d& y; [" f1 t" T   // Gather the protocols and start the freshly assembled P2P server8 M2 X4 m. ]0 b5 y* ?9 @
   for _, service := range services {  U2 O# x4 p6 }- B2 g" X
           running.Protocols = append(running.Protocols, service.Protocols()...)
6 b; x8 a: |/ s. s9 f! Y% K   }
; D  p# j3 _* e. z   if err := running.Start(); err != nil { //见下面的(srv *Server)Start方法% ^( t9 i+ F0 N
           return convertFileLockError(err)) t# n2 E+ t! e
   }
% Z: C% [5 ?6 ~; n, f   // Start each of the services
7 ^) @" B; g& w) O! B   started := []reflect.Type{}
4 O& Z% l2 L, ~; X7 W   for kind, service := range services {
; h: x, E6 L# R; A" m           // Start the next service, stopping all previous upon failure
% k; O# N7 ~2 x% C) `; U1 c$ ~           //启动每个services通过下面的方法func (s *Ethereum) Start(srvr *p2p.Server) error {2 L  ^6 \; w( Q1 a+ n9 P2 X
           if err := service.Start(running); err != nil {7 G3 p2 k4 F" U$ i
                   for _, kind := range started {2 x8 W# d. V$ a, O' j6 T7 w) I
                           services[kind].Stop()
+ l$ M2 T+ e8 d                   }4 K  \0 d  m1 E
                   running.Stop(). s8 s0 f9 R! X: y) H& Y  x
                   return err
6 f7 b$ D0 \" h4 o  m* s1 o4 Y- B2 [           }
( q  v* C7 z) N  k0 {           ...4 N2 t. y+ u2 t1 w
   }
7 w. ^7 `8 H$ c2 W* A}) I* ^: O# f$ L; j
// Start starts running the server.
  F3 X2 c( J: y// Servers can not be re-used after stopping.
# |$ u' x+ q" j/ efunc (srv *Server) Start() (err error) {
0 `- t7 a- c! Y, k1 ~' a   srv.lock.Lock(): n- K5 O/ p7 |+ G. u/ I* b
   //srv.lock为了避免多线程重复启动
# }; q: }; R2 N1 ~) v   defer srv.lock.Unlock(), X5 a4 V7 r- k$ r! U7 Q
   if srv.running {
$ g1 X: a# x; m2 T' H1 a9 q& D' i9 d           return errors.New("server already running")
: n$ @+ N& s, n   }7 B. v: ~9 ?6 w! J3 J+ F* r" x, _
   srv.running = true. ]4 h8 q; L6 }3 ~
   srv.log = srv.Config.Logger
$ o4 Z- t" {4 U% E: y   if srv.log == nil {
* z5 u9 G! \4 }" [- z5 T           srv.log = log.New()
8 z/ d6 Q, r" N# L: U8 x   }" _$ F% g% E3 I1 L/ ^6 n8 o3 k5 D
   if srv.NoDial && srv.ListenAddr == "" {3 G1 D9 L7 s! Z- U9 ]; G; f! _
           srv.log.Warn("P2P server will be useless, neither dialing nor listening")
0 Q% u+ q. X) J6 s, \% k   }# _0 v" @2 H) ]' |
   // static fields
, ?; M, m2 l6 P5 V) O9 A   if srv.PrivateKey == nil {! e3 T# x" _3 L, [% f
           return fmt.Errorf("Server.PrivateKey must be set to a non-nil key"), x+ z$ p! \3 g" ?) U1 \2 D" G5 G
   }
& ~8 w# Q1 l" p8 Z3 f   //newTransport使用了newRLPX使用了rlpx.go中的网络协议。
# B: o5 t1 \. V; V  x( m) X   if srv.newTransport == nil {
7 q  |6 z* T7 f) k, r, ]           srv.newTransport = newRLPX
' L9 D; c: g+ r; d: h% E   }9 ^/ s  z: F9 e0 b& l5 |
   if srv.Dialer == nil {
6 i9 X+ x* P$ I           srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
: i5 w$ V2 b6 Y/ Y   }: j  N/ u) U3 Y, K2 B3 d
   srv.quit = make(chan struct{})/ a) @- F' J4 j: E! u4 l% V+ Y
   srv.addpeer = make(chan *conn)
. W( T( w0 w" _* Q: E  Q* G   srv.delpeer = make(chan peerDrop)) E- }5 e3 p" \1 S% k- |+ V
   srv.posthandshake = make(chan *conn)* I7 U6 ]& O7 X+ L% k, H
   srv.addstatic = make(chan *enode.Node)
) P; H; x( o0 r$ W   srv.removestatic = make(chan *enode.Node)+ Y7 m$ d+ t- t5 a# [  c. `7 }
   srv.addtrusted = make(chan *enode.Node)
* p7 r. o9 f& t# N   srv.removetrusted = make(chan *enode.Node)4 J5 T( b2 F- p& T- ?
   srv.peerOp = make(chan peerOpFunc), ~: T3 i  O4 O" g8 o' |  r2 Q
   srv.peerOpDone = make(chan struct{})
9 y: g) ?7 u: Q/ H. Q3 s1 n   //srv.setupLocalNode()这里主要执行握手1 m" d% n# _) j. z$ `9 x' q
   if err := srv.setupLocalNode(); err != nil {. T/ H+ T4 Q4 O. A
           return err
6 d! J: y; J9 q8 z1 F: _  i. @% w   }0 D+ H# h+ |$ K7 e2 u6 @4 \& b
   if srv.ListenAddr != "" {
4 E2 L  |4 `5 q: j5 x' g' E2 `0 Y           //监听TCP端口-->用于业务数据传输,基于RLPx协议)
: A' e+ Y) @9 }, C& v; n' h9 g, S" D           //在setupListening中有个go srv.listenLoop()去监听某个端口有无主动发来的IP连接  T: L) U/ I5 R: R/ n
           if err := srv.setupListening(); err != nil {+ |7 H  [7 N3 J
                   return err
- ~7 o4 M$ Y' F! a# g+ b9 @) [5 O: H. C           }
3 ^7 a: X2 x, l" R  {1 n1 p   }
% V6 b* C+ T: Z6 u4 w   //侦听UDP端口(用于结点发现内部会启动goroutine)
5 h2 l) T+ B9 G   if err := srv.setupDiscovery(); err != nil {$ L0 K! f& m! `$ E- r
           return err- _; Q1 Y/ K3 ?' [8 u: L
   }
* I7 o% N3 u' Q# m* E   dynPeers := srv.maxDialedConns()! t+ a( ]/ \( ]  M! O) `
   dialer := newDialState(srv.localnode.ID(), srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
  Z; F6 \& l  o4 h  L# D   srv.loopWG.Add(1)
/ f* K9 U2 V# H5 i8 }; ?   // 启动新线程发起TCP连接请求
( m( v' I  o1 \   //在run()函数中,监听srv.addpeer通道有没有信息如果有远端peer发来连接请求,
2 D% I0 u, L1 W* @1 F- q+ S   //则调用Server.newPeer()生成新的peer对象,并把Server.Protocols全交给peer。
0 Z  F3 r5 C% v/ ]; n6 d3 E   /*; {' X9 r: [, e8 W
   case c :=  0 {
0 d  m) M1 c; m) e* s           if s.config.LightPeers >= srvr.MaxPeers {
1 C* A0 y9 Z5 U; p9 H                   return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers)
! w4 V3 ^8 }' S6 O           }/ b; t2 j  Q" L" g
           maxPeers -= s.config.LightPeers# S" E$ \  B* w- N
   }
' d6 _& j& |" I   // Start the networking layer and the light server if requested
* r# J& n1 y5 y% a   s.protocolManager.Start(maxPeers)
3 r& y3 O8 N" E, ^- k% k   if s.lesServer != nil {
9 Q# ?: G. q2 {$ f3 w0 D' `* v* }           s.lesServer.Start(srvr)
# ~* T, S& @9 h4 e- A; o   }+ k% T8 V' F) ]5 b$ W- G% Y
   return nil
. A0 X1 {3 K5 f& I- ?: e}
8 x: ~0 [9 f5 X5 n$ r/eth/handler.go* s3 }' e8 @5 T1 _( W3 S
type ProtocolManager struct {
5 v8 l; T4 }: A8 m- r   networkID uint649 }2 y( F7 R2 b
   fastSync  uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)' f/ x1 V1 X* z) Y3 M
   acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
$ q% Y3 I  I; ~. p9 _% K* ^/ A  d   txpool      txPool8 c1 [( v" L# s
   blockchain  *core.BlockChain! \  [" k/ j8 m1 I7 n$ o3 X/ K$ y
   chainconfig *params.ChainConfig! C, g% b( I4 [9 A" I5 [
   maxPeers    int' S7 Z/ V( D% k5 O. U8 ^) ^
   //Downloader类型成员负责所有向相邻个体主动发起的同步流程。8 J- f) z. ~6 ?  a6 n
   downloader *downloader.Downloader
# P  Y5 A* u$ t# c   //Fetcher类型成员累积所有其他个体发送来的有关新数据的宣布消息,并在自身对照后做出安排
) o  @7 d+ f% R: B   fetcher    *fetcher.Fetcher
; A$ Q% r1 ?5 U5 J. {) _, C   //用来缓存相邻个体列表,peer{}表示网络中的一个远端个体。
$ V; D- n) J& A# P8 Z  t* _   peers      *peerSet0 x: U' |% m* F
   SubProtocols []p2p.Protocol2 w- C0 {% ~% W" `# p
   eventMux      *event.TypeMux
8 Q  K$ b, D4 K. c   txsCh         chan core.NewTxsEvent8 X5 d) Y: y& y: P8 x  V
   txsSub        event.Subscription
2 {9 r! b6 |/ |: P6 [0 A   minedBlockSub *event.TypeMuxSubscription
7 I: N9 p8 Z' ~   4 S2 W) @. ^+ i8 H, U1 s3 M
   //通过各种通道(chan)和事件订阅(subscription)的方式,接收和发送包括交易和区块在内的数据更新。
. d* l+ _$ _* g1 L1 G/ ~   //当然在应用中,订阅也往往利用通道来实现事件通知。
9 D! `. {& `, x/ j' r. n* a& a   // channels for fetcher, syncer, txsyncLoop
; ]/ P! C  T, @  l* k( h   newPeerCh   chan *peer
9 p9 N; Q) V/ I" h   txsyncCh    chan *txsync
$ U- l3 h+ F+ P  t   quitSync    chan struct{}$ G* x2 I; O9 V3 \
   noMorePeers chan struct{}1 c* e  A2 j& m; j' T. y
   // wait group is used for graceful shutdowns during downloading
! u8 D! |  U5 y6 c/ v) g) T* c   // and processing. s6 k+ n( v, C1 b& L% @& ?
   wg sync.WaitGroup
9 L/ j$ n1 z  l# P/ z* s8 |8 [}, @) l1 o, e7 w( l4 T( B* U' g8 j
   Start()函数是ProtocolManager的启动函数,它会在eth.Ethereum.Start()中被主动调用。, k2 I( o+ o: }& U
ProtocolManager.Start()会启用4个单独线程(goroutine,协程)去分别执行4个函数,
: [6 u% c2 P2 l1 K/ ?' a这也标志着该以太坊个体p2p通信的全面启动。
6 M# W; }( d. ffunc (pm *ProtocolManager) Start(maxPeers int) {3 u3 Q: ~2 Q# m( R1 M) j
   pm.maxPeers = maxPeers
* X* X+ }, {+ M# n   // broadcast transactions
) h- W0 r* `& ]/ b, A   //广播交易的通道。 txsCh会作为txpool的TxPreEvent订阅通道。
+ v2 w, K$ u7 I   //txpool有了这种消息会通知给这个txsCh。 广播交易的goroutine会把这个消息广播出去。
0 g7 s9 m" W+ {   pm.txsCh = make(chan core.NewTxsEvent, txChanSize)  ~0 t& N0 R# f; k, f  i+ y
   //订阅交易信息
  e/ i8 w. M' p/ ?; G: K3 j1 Q   pm.txsSub = pm.txpool.SubscribeNewTxsEvent(pm.txsCh)( w# Q" u0 I0 x3 K
   
2 `, T* L# ~  i4 [0 }5 |! {; w  ]   go pm.txBroadcastLoop()
) `+ J/ n3 P% H0 L   //订阅挖矿消息。当新的Block被挖出来的时候会产生消息! I3 j5 C. r$ N8 w% z
   // broadcast mined blocks
- k9 I1 j- B4 g6 [4 R   pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{})
4 B1 [3 l, X0 H  l: Q) [% W   //挖矿广播 goroutine 当挖出来的时候需要尽快的广播到网络上面去。: d. @- Z9 A7 D- `9 D4 \  _  N0 H
   go pm.minedBroadcastLoop()
: @: w2 R  Y+ g0 z2 |8 i5 V' `   // start sync handlers# b$ {0 Q, b2 J/ l. r$ _5 P. k
   // 同步器负责周期性地与网络同步,下载散列和块以及处理通知处理程序。4 a& Z5 ^7 M% V% d, M
   go pm.syncer()
" ~3 @  J$ ^" s* z   // txsyncLoop负责每个新连接的初始事务同步。 当新的peer出现时,
7 r& d$ u" s+ q3 `$ ?   // 转发所有当前待处理的事务。为了最小化出口带宽使用,我们一次只发送一个小包。& l8 x1 x& U* @' ]7 Z' y$ i
   go pm.txsyncLoop()
( E5 h1 x8 V, a4 X* m}
$ v* ?$ l4 {$ @) ~; T//txBroadcastLoop()会在txCh通道的收端持续等待,一旦接收到有关新交易的事件,
4 W; M2 @0 @, b: T8 C   //会立即调用BroadcastTx()函数广播给那些尚无该交易对象的相邻个体。6 N5 d0 Y" e3 v
//------------------go pm.txBroadcastLoop()-----------------------
& w" b0 F7 ]: b! G2 i- _func (pm *ProtocolManager) txBroadcastLoop() {
; I% w9 N+ [8 O, b! U0 X/ C   for {; S1 {1 G! k) ]& ~0 [7 P( f6 b" p, E
           select {8 L* S6 B* [$ V# P% _, o; d
           case event := = pm.maxPeers && !p.Peer.Info().Network.Trusted {. T5 {: |- N0 i, A0 t0 ?1 |
           return p2p.DiscTooManyPeers
+ M5 [! J3 q3 ]& l' `6 F   }4 n6 `0 _: `; T  q6 W4 ^7 A
   p.Log().Debug("Ethereum peer connected", "name", p.Name())' h! E6 H/ Y: H. T  u
   // Execute the Ethereum handshake" s) ?& {. k# K, g( S3 x6 s5 L
   var (2 M) i! P2 g6 K) i; l
           genesis = pm.blockchain.Genesis()) a, [1 {( `, B; G" ]% ?. |
           head    = pm.blockchain.CurrentHeader()$ \; ]3 m( J  s9 v% R7 v& y
           hash    = head.Hash(). b7 i1 f' h  |  d& K/ s
           number  = head.Number.Uint64()$ y  t  V7 Y/ l$ Z
           td      = pm.blockchain.GetTd(hash, number)3 f" Z9 q+ _/ b* J& x
   )
$ t! p, j. d6 Y0 b/ |   //握手,与对方peer沟通己方的区块链状态
* T. L$ Q4 `3 C   if err := p.Handshake(pm.networkID, td, hash, genesis.Hash()); err != nil {7 ^$ Q! g* l4 {7 k0 y2 O7 \
           p.Log().Debug("Ethereum handshake failed", "err", err)+ T# y& E) t* _/ q: _
           return err  ?  k, t' g, `1 W
   }1 n" [3 x6 O, j& E% s+ R
   //初始化一个读写通道,用以跟对方peer相互数据传输。
3 m: n3 ^) L( L) r& c+ I   if rw, ok := p.rw.(*meteredMsgReadWriter); ok {& a* M& s$ D' ^2 L. ?0 ?8 k: s6 e+ w
           rw.Init(p.version)
$ N! f# j8 B+ G) Q& A7 ^) i   }! W+ V! k" e9 A# N, p
   // Register the peer locally
3 N3 V( @. m& [! n   //注册对方peer,存入己方peer列表;只有handle()函数退出时,才会将这个peer移除出列表。
7 v! z+ f9 C( Q8 {   if err := pm.peers.Register(p); err != nil {
$ s' ]# _6 d/ D           p.Log().Error("Ethereum peer registration failed", "err", err)2 _' A- K6 [6 k$ v. U- w  O
           return err
3 m1 E0 ]* ?$ p# y9 d! V7 `2 \   }
3 o( b/ T' f0 Q/ N2 [* I+ m! L   defer pm.removePeer(p.id)
1 z, X* C' d" n; W6 ]4 F" q   //Downloader成员注册这个新peer;Downloader会自己维护一个相邻peer列表。
2 m! G' |2 @& i# d0 A9 g; ~   // Register the peer in the downloader. If the downloader considers it banned, we disconnect
+ f9 |$ c; j5 j* i& k   if err := pm.downloader.RegisterPeer(p.id, p.version, p); err != nil {
# [  H% S* T7 H& B& \0 R. ~           return err) k/ U6 F% J2 K; n
   }
+ K: v, R$ }. r/ \! ]" w   // Propagate existing transactions. new transactions appearing
* I9 ], h8 R) {, j   // after this will be sent via broadcasts.
* r% i5 a! x  R' b   /*
7 Q! c8 n; [5 k& V0 i# E   调用syncTransactions(),用当前txpool中新累计的tx对象组装成一个txsync{}对象,
  p% r( x- p- B# e+ L8 p   推送到内部通道txsyncCh。还记得Start()启动的四个函数么? 其中第四项txsyncLoop()  i; z7 Q% ?& p( r# v7 O; ]
   中用以等待txsync{}数据的通道txsyncCh,正是在这里被推入txsync{}的。& k5 J2 g4 N; S2 |% M) Q' ^: g
   */* w1 F) r- _+ n, o, r. a. ~
   pm.syncTransactions(p)
8 u3 G9 Q- t! O, ^9 K   // If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork
% W* ~7 }* j4 o0 s% \) @# O   if daoBlock := pm.chainconfig.DAOForkBlock; daoBlock != nil {) a7 v! Z( Y' ^+ x1 ]
           // Request the peer's DAO fork header for extra-data validation: Q) g+ R( ^) n. ~3 |' r" w, s& f$ f
           if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil {9 q  D4 f& v: c( q) t' n* r
                   return err
7 C4 x" D0 G! ~. r! X  a           }
$ \" G5 T* E2 W& p' B           // Start a timer to disconnect if the peer doesn't reply in time
/ d% ~* A0 ~: G* l  u$ a           p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {3 y+ ~' C- \2 h
                   p.Log().Debug("Timed out DAO fork-check, dropping")
3 t- D8 }5 m" U- X                   pm.removePeer(p.id)6 b- h1 D# D  ?5 W* u  c9 B7 o
           })# @8 L* f1 Y& k5 y. C9 j& _5 P
           // Make sure it's cleaned up if the peer dies off
5 S! B) F: G# ^. h( `1 b           defer func() {9 `$ n6 f9 F+ R9 ^
                   if p.forkDrop != nil {9 P' W+ g0 b+ @. @4 g* L( `
                           p.forkDrop.Stop()* o2 C6 R3 J) k" q# p: K2 V
                           p.forkDrop = nil
  t+ e0 C8 A. o2 p: {* {                   }
; b, m/ l# [% P           }()
" R  `5 i# P  c& X& f3 d   }: g0 ]. w: I6 e, U) b1 s8 O
           //在无限循环中启动handleMsg(),当对方peer发出任何msg时,- b, }3 K+ g: f, T. G5 \. L
           //handleMsg()可以捕捉相应类型的消息并在己方进行处理。- k3 Q7 ?8 e' D
   // main loop. handle incoming messages.' M  p' u4 C7 H) C# j6 X
   for {
7 M3 z6 X$ l  o( n* I5 o           if err := pm.handleMsg(p); err != nil {
3 T) s. T$ a. Z; x                   p.Log().Debug("Ethereum message handling failed", "err", err)
' q' `3 s5 I- L: _                   return err* O: J: ~6 O* K+ z0 C% I1 U8 U
           }: Q  e  B8 C4 ~+ t& v
   }
+ o! ]2 H) v6 r# P1 Y9 h9 V}
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

Mohammad61417 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    2