github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/dial.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:40</date> 10 //</624450102777352192> 11 12 13 package p2p 14 15 import ( 16 "container/heap" 17 "errors" 18 "fmt" 19 "net" 20 "time" 21 22 "github.com/ethereum/go-ethereum/log" 23 "github.com/ethereum/go-ethereum/p2p/enode" 24 "github.com/ethereum/go-ethereum/p2p/netutil" 25 ) 26 27 const ( 28 //这是介于 29 //重拨某个节点。 30 dialHistoryExpiration = 30 * time.Second 31 32 //发现查找受到限制,只能运行 33 //每隔几秒钟一次。 34 lookupInterval = 4 * time.Second 35 36 //如果在这段时间内找不到对等点,则初始引导节点为 37 //试图连接。 38 fallbackInterval = 20 * time.Second 39 40 //端点分辨率通过有界退避进行限制。 41 initialResolveDelay = 60 * time.Second 42 maxResolveDelay = time.Hour 43 ) 44 45 //nodeadialer用于连接到网络中的节点,通常使用 46 //一个底层的net.dialer,但在测试中也使用了net.pipe 47 type NodeDialer interface { 48 Dial(*enode.Node) (net.Conn, error) 49 } 50 51 //tcpDialer通过使用net.dialer 52 //创建到网络中节点的TCP连接 53 type TCPDialer struct { 54 *net.Dialer 55 } 56 57 //拨号创建到节点的TCP连接 58 func (t TCPDialer) Dial(dest *enode.Node) (net.Conn, error) { 59 addr := &net.TCPAddr{IP: dest.IP(), Port: dest.TCP()} 60 return t.Dialer.Dial("tcp", addr.String()) 61 } 62 63 //拨号状态计划拨号和查找。 64 //每次迭代都有机会计算新任务 65 //在server.run中的主循环。 66 type dialstate struct { 67 maxDynDials int 68 ntab discoverTable 69 netrestrict *netutil.Netlist 70 self enode.ID 71 72 lookupRunning bool 73 dialing map[enode.ID]connFlag 74 lookupBuf []*enode.Node //当前发现查找结果 75 randomNodes []*enode.Node //从表中填充 76 static map[enode.ID]*dialTask 77 hist *dialHistory 78 79 start time.Time //拨号器首次使用的时间 80 bootnodes []*enode.Node //没有对等机时的默认拨号 81 } 82 83 type discoverTable interface { 84 Close() 85 Resolve(*enode.Node) *enode.Node 86 LookupRandom() []*enode.Node 87 ReadRandomNodes([]*enode.Node) int 88 } 89 90 //拨号历史记录会记住最近的拨号。 91 type dialHistory []pastDial 92 93 //PastDial是拨号历史记录中的一个条目。 94 type pastDial struct { 95 id enode.ID 96 exp time.Time 97 } 98 99 type task interface { 100 Do(*Server) 101 } 102 103 //为所拨的每个节点生成一个拨号任务。它的 104 //任务运行时无法访问字段。 105 type dialTask struct { 106 flags connFlag 107 dest *enode.Node 108 lastResolved time.Time 109 resolveDelay time.Duration 110 } 111 112 //discovertask运行发现表操作。 113 //任何时候只有一个discovertask处于活动状态。 114 //discovertask.do执行随机查找。 115 type discoverTask struct { 116 results []*enode.Node 117 } 118 119 //如果没有其他任务,则生成waitexpiretask 120 //在server.run中保持循环。 121 type waitExpireTask struct { 122 time.Duration 123 } 124 125 func newDialState(self enode.ID, static []*enode.Node, bootnodes []*enode.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate { 126 s := &dialstate{ 127 maxDynDials: maxdyn, 128 ntab: ntab, 129 self: self, 130 netrestrict: netrestrict, 131 static: make(map[enode.ID]*dialTask), 132 dialing: make(map[enode.ID]connFlag), 133 bootnodes: make([]*enode.Node, len(bootnodes)), 134 randomNodes: make([]*enode.Node, maxdyn/2), 135 hist: new(dialHistory), 136 } 137 copy(s.bootnodes, bootnodes) 138 for _, n := range static { 139 s.addStatic(n) 140 } 141 return s 142 } 143 144 func (s *dialstate) addStatic(n *enode.Node) { 145 //这将覆盖任务,而不是更新现有的 146 //输入,让用户有机会强制执行解决操作。 147 s.static[n.ID()] = &dialTask{flags: staticDialedConn, dest: n} 148 } 149 150 func (s *dialstate) removeStatic(n *enode.Node) { 151 //这将删除一个任务,因此将来不会尝试连接。 152 delete(s.static, n.ID()) 153 //这将删除以前的拨号时间戳,以便应用程序 154 //可以强制服务器立即与所选对等机重新连接。 155 s.hist.remove(n.ID()) 156 } 157 158 func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Time) []task { 159 if s.start.IsZero() { 160 s.start = now 161 } 162 163 var newtasks []task 164 addDial := func(flag connFlag, n *enode.Node) bool { 165 if err := s.checkDial(n, peers); err != nil { 166 log.Trace("Skipping dial candidate", "id", n.ID(), "addr", &net.TCPAddr{IP: n.IP(), Port: n.TCP()}, "err", err) 167 return false 168 } 169 s.dialing[n.ID()] = flag 170 newtasks = append(newtasks, &dialTask{flags: flag, dest: n}) 171 return true 172 } 173 174 //计算此时所需的动态拨号数。 175 needDynDials := s.maxDynDials 176 for _, p := range peers { 177 if p.rw.is(dynDialedConn) { 178 needDynDials-- 179 } 180 } 181 for _, flag := range s.dialing { 182 if flag&dynDialedConn != 0 { 183 needDynDials-- 184 } 185 } 186 187 //每次调用时使拨号历史记录过期。 188 s.hist.expire(now) 189 190 //如果静态节点未连接,则为其创建拨号。 191 for id, t := range s.static { 192 err := s.checkDial(t.dest, peers) 193 switch err { 194 case errNotWhitelisted, errSelf: 195 log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()}, "err", err) 196 delete(s.static, t.dest.ID()) 197 case nil: 198 s.dialing[id] = t.flags 199 newtasks = append(newtasks, t) 200 } 201 } 202 //如果我们没有任何对等点,请尝试拨打随机引导节点。这个 203 //场景对于发现 204 //桌子上可能满是坏同学,很难找到好同学。 205 if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && now.Sub(s.start) > fallbackInterval { 206 bootnode := s.bootnodes[0] 207 s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...) 208 s.bootnodes = append(s.bootnodes, bootnode) 209 210 if addDial(dynDialedConn, bootnode) { 211 needDynDials-- 212 } 213 } 214 //将表中的随机节点用于所需的一半 215 //动态拨号。 216 randomCandidates := needDynDials / 2 217 if randomCandidates > 0 { 218 n := s.ntab.ReadRandomNodes(s.randomNodes) 219 for i := 0; i < randomCandidates && i < n; i++ { 220 if addDial(dynDialedConn, s.randomNodes[i]) { 221 needDynDials-- 222 } 223 } 224 } 225 //从随机查找结果创建动态拨号,已尝试删除 226 //结果缓冲区中的项。 227 i := 0 228 for ; i < len(s.lookupBuf) && needDynDials > 0; i++ { 229 if addDial(dynDialedConn, s.lookupBuf[i]) { 230 needDynDials-- 231 } 232 } 233 s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])] 234 //如果需要更多候选项,则启动查找。 235 if len(s.lookupBuf) < needDynDials && !s.lookupRunning { 236 s.lookupRunning = true 237 newtasks = append(newtasks, &discoverTask{}) 238 } 239 240 //启动计时器,等待下一个节点全部过期 241 //候选人已被试用,目前没有活动任务。 242 //这样可以防止拨号程序逻辑没有勾选的情况发生。 243 //因为没有挂起的事件。 244 if nRunning == 0 && len(newtasks) == 0 && s.hist.Len() > 0 { 245 t := &waitExpireTask{s.hist.min().exp.Sub(now)} 246 newtasks = append(newtasks, t) 247 } 248 return newtasks 249 } 250 251 var ( 252 errSelf = errors.New("is self") 253 errAlreadyDialing = errors.New("already dialing") 254 errAlreadyConnected = errors.New("already connected") 255 errRecentlyDialed = errors.New("recently dialed") 256 errNotWhitelisted = errors.New("not contained in netrestrict whitelist") 257 ) 258 259 func (s *dialstate) checkDial(n *enode.Node, peers map[enode.ID]*Peer) error { 260 _, dialing := s.dialing[n.ID()] 261 switch { 262 case dialing: 263 return errAlreadyDialing 264 case peers[n.ID()] != nil: 265 return errAlreadyConnected 266 case n.ID() == s.self: 267 return errSelf 268 case s.netrestrict != nil && !s.netrestrict.Contains(n.IP()): 269 return errNotWhitelisted 270 case s.hist.contains(n.ID()): 271 return errRecentlyDialed 272 } 273 return nil 274 } 275 276 func (s *dialstate) taskDone(t task, now time.Time) { 277 switch t := t.(type) { 278 case *dialTask: 279 s.hist.add(t.dest.ID(), now.Add(dialHistoryExpiration)) 280 delete(s.dialing, t.dest.ID()) 281 case *discoverTask: 282 s.lookupRunning = false 283 s.lookupBuf = append(s.lookupBuf, t.results...) 284 } 285 } 286 287 func (t *dialTask) Do(srv *Server) { 288 if t.dest.Incomplete() { 289 if !t.resolve(srv) { 290 return 291 } 292 } 293 err := t.dial(srv, t.dest) 294 if err != nil { 295 log.Trace("Dial error", "task", t, "err", err) 296 //如果拨号失败,请尝试解析静态节点的ID。 297 if _, ok := err.(*dialError); ok && t.flags&staticDialedConn != 0 { 298 if t.resolve(srv) { 299 t.dial(srv, t.dest) 300 } 301 } 302 } 303 } 304 305 //解决查找目标的当前终结点的尝试 306 //使用发现。 307 // 308 //解决操作通过后退进行节流,以避免淹没 309 //对不存在的节点进行无用查询的发现网络。 310 //当找到节点时,退避延迟重置。 311 func (t *dialTask) resolve(srv *Server) bool { 312 if srv.ntab == nil { 313 log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled") 314 return false 315 } 316 if t.resolveDelay == 0 { 317 t.resolveDelay = initialResolveDelay 318 } 319 if time.Since(t.lastResolved) < t.resolveDelay { 320 return false 321 } 322 resolved := srv.ntab.Resolve(t.dest) 323 t.lastResolved = time.Now() 324 if resolved == nil { 325 t.resolveDelay *= 2 326 if t.resolveDelay > maxResolveDelay { 327 t.resolveDelay = maxResolveDelay 328 } 329 log.Debug("Resolving node failed", "id", t.dest.ID, "newdelay", t.resolveDelay) 330 return false 331 } 332 //找到节点。 333 t.resolveDelay = initialResolveDelay 334 t.dest = resolved 335 log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()}) 336 return true 337 } 338 339 type dialError struct { 340 error 341 } 342 343 //拨号执行实际连接尝试。 344 func (t *dialTask) dial(srv *Server, dest *enode.Node) error { 345 fd, err := srv.Dialer.Dial(dest) 346 if err != nil { 347 return &dialError{err} 348 } 349 mfd := newMeteredConn(fd, false, dest.IP()) 350 return srv.SetupConn(mfd, t.flags, dest) 351 } 352 353 func (t *dialTask) String() string { 354 id := t.dest.ID() 355 return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], t.dest.IP(), t.dest.TCP()) 356 } 357 358 func (t *discoverTask) Do(srv *Server) { 359 //每当动态拨号 360 //必要的。查找需要花费一些时间,否则 361 //事件循环旋转过快。 362 next := srv.lastLookup.Add(lookupInterval) 363 if now := time.Now(); now.Before(next) { 364 time.Sleep(next.Sub(now)) 365 } 366 srv.lastLookup = time.Now() 367 t.results = srv.ntab.LookupRandom() 368 } 369 370 func (t *discoverTask) String() string { 371 s := "discovery lookup" 372 if len(t.results) > 0 { 373 s += fmt.Sprintf(" (%d results)", len(t.results)) 374 } 375 return s 376 } 377 378 func (t waitExpireTask) Do(*Server) { 379 time.Sleep(t.Duration) 380 } 381 func (t waitExpireTask) String() string { 382 return fmt.Sprintf("wait for dial hist expire (%v)", t.Duration) 383 } 384 385 //仅使用这些方法访问或修改拨号历史记录。 386 func (h dialHistory) min() pastDial { 387 return h[0] 388 } 389 func (h *dialHistory) add(id enode.ID, exp time.Time) { 390 heap.Push(h, pastDial{id, exp}) 391 392 } 393 func (h *dialHistory) remove(id enode.ID) bool { 394 for i, v := range *h { 395 if v.id == id { 396 heap.Remove(h, i) 397 return true 398 } 399 } 400 return false 401 } 402 func (h dialHistory) contains(id enode.ID) bool { 403 for _, v := range h { 404 if v.id == id { 405 return true 406 } 407 } 408 return false 409 } 410 func (h *dialHistory) expire(now time.Time) { 411 for h.Len() > 0 && h.min().exp.Before(now) { 412 heap.Pop(h) 413 } 414 } 415 416 //堆接口样板 417 func (h dialHistory) Len() int { return len(h) } 418 func (h dialHistory) Less(i, j int) bool { return h[i].exp.Before(h[j].exp) } 419 func (h dialHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 420 func (h *dialHistory) Push(x interface{}) { 421 *h = append(*h, x.(pastDial)) 422 } 423 func (h *dialHistory) Pop() interface{} { 424 old := *h 425 n := len(old) 426 x := old[n-1] 427 *h = old[0 : n-1] 428 return x 429 } 430