Blockchain tutorials ethereum source code analysis p2p-dial.go source code analysis

Source: Internet
Author: User
Dial. Go is mainly responsible for establishing links in P2P. For example, a node that establishes a link is found. Establish a connection with the node. Use discover to find the address of the specified node. .
Dial. Go uses a dailstate data structure to store intermediate states. It is the core data structure in the dial function.
// Dialstate schedules dials and discovery lookups.
// It get's a chance to compute new tasks on every iteration
// Of the Main Loop in server. Run.
Type dialstate struct {
Maxdyndials int // maximum number of dynamic node links
Ntab discovertable // discovertable used for node Query
Netrestrict * netutil. netlist

Lookuprunning bool dialing map [discover. nodeid] connflag // The node that is being linked to lookupbuf [] * discover. node // current discovery lookup results // The current discovery query result randomnodes [] * discover. node // filled from Table // The Node static map [discover. nodeid] * dialtask // static node. Hist * dialhistory start time. Time // time when the dialer was first used bootnodes [] * discover. node // default dials when there are no peers // This is a built-in node. If no other node is found. Link these nodes .}

Dailstate creation process.

func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {    s := &dialstate{        maxDynDials: maxdyn,        ntab:        ntab,        netrestrict: netrestrict,        static:      make(map[discover.NodeID]*dialTask),        dialing:     make(map[discover.NodeID]connFlag),        bootnodes:   make([]*discover.Node, len(bootnodes)),        randomNodes: make([]*discover.Node, maxdyn/2),        hist:        new(dialHistory),    }    copy(s.bootnodes, bootnodes)    for _, n := range static {        s.addStatic(n)    }    return s}

The most important method of Dail is the newtasks method. This method is used to generate tasks. Task is an interface. There is a do method.

Type task interface {do (* server)} func (S * dialstate) newtasks (nrunning int, peers map [discover. nodeid] * peer, now time. time) [] task {if S. start = (time. time {}) {S. start = now} var newtasks [] task // adddial is an internal method. First, check the node through checkdial. Set the status and add the node to the newtasks queue. Adddial: = func (flag connflag, N * discover. node) bool {If err: = S. checkdial (n, peers); Err! = Nil {log. trace ("Skipping dial candidate", "ID", N. ID, "ADDR", & net. tcpaddr {IP: N. IP, Port: int (N. TCP)}, "Err", err) return false} s. dialing [n. id] = Flag newtasks = append (newtasks, & dialtask {flags: Flag, DEST: n}) return true} // compute number of dynamic dials necessary at this point. needdyndials: = S. maxdyndials // first judge the type of the established connection. If it is a dynamic type. Therefore, the number of dynamic connections needs to be reduced. For _, P: = range peers {If P. RW. Is (dyndialedconn) {needdyndials --} // then judge the connection being established. If it is a dynamic type. Therefore, the number of dynamic connections needs to be reduced. For _, flag: = range S. Dialing {if flag & dyndialedconn! = 0 {needdyndials --} // expire the dial history on every invocation. s. hist. expire (now) // create dials for static nodes if they are not connected. // view all static types. If possible, create a link. For ID, T: = range S. static {err: = S. checkdial (T. deST, peers) Switch err {Case errnotwhitelisted, errself: log. warn ("removing static dial candidate", "ID", T. deST. ID, "ADDR", & net. tcpaddr {IP: T. deST. IP, Port: int (T. deST. TCP)}, "Err", err) Delete (S. static, T. deST. ID) Case nil: S. dialing [ID] = T. flags newtasks = append (newtasks, T)} // if we don't have any peers whatsoever, try to dial a rando M bootnode. this // scenario is useful for the testnet (and private networks) where the discovery // table might be full of mostly bad peers, making it hard to find good ones. // if no link exists. And no link is created within 20 seconds (fallbackinterval. Use bootnode to create a link. If Len (peers) = 0 & Len (S. bootnodes)> 0 & needdyndials> 0 & now. sub (S. start)> fallbackinterval {bootnode: = S. bootnodes [0] S. bootnodes = append (S. bootnodes [: 0], S. bootnodes [1:]...) s. bootnodes = append (S. bootnodes, bootnode) If adddial (dyndialedconn, bootnode) {needdyndials --} // use random nodes from the table for half of the necessary // dynamic dials. // otherwise, use 1/2 random nodes to create a link. Randomcandidates: = needdyndials/2 If randomcandidates> 0 {n: = S. ntab. readrandomnodes (S. randomnodes) for I: = 0; I <randomcandidates & I <n; I ++ {If adddial (dyndialedconn, S. randomnodes [I]) {needdyndials -- }}// create dynamic Dials from random lookup results, removing tried // items from the result buffer. i: = 0 for; I <Len (S. lookupbuf) & needdyndials> 0; I ++ {If adddial (dyndia Ledconn, S. lookupbuf [I]) {needdyndials --} s. lookupbuf = S. lookupbuf [: Copy (S. lookupbuf, S. lookupbuf [I:])] // launch a discovery lookup if more candidates are needed. // if this is the case, you cannot create enough dynamic links. Create a discovertask to search for other nodes on the network. Put lookupbuf if Len (S. lookupbuf) <needdyndials &&! S. lookuprunning {S. lookuprunning = true newtasks = append (newtasks, & discovertask {})} // launch a timer to wait for the next node to expire if all // candidates have been tried and no task is currently active. // This shoshould prevent cases where the dialer logic is not ticked // because there are no pending events. // if no task is required, a sleeping task is created and returned. If nrunning = 0 & Len (newtasks) = 0 & S. hist. len ()> 0 {T: = & waitexpiretask {S. hist. min (). exp. sub (now)} newtasks = append (newtasks, T)} return newtasks}

The checkdial method is used to check whether a link needs to be created for a task.

Func (S * dialstate) checkdial (N * discover. node, peers map [discover. nodeid] * peer) error {_, dialing: = S. dialing [n. id] switch {Case Dialing: // creating return erralreadydialing case peers [n. id]! = Nil: // return erralreadyconnected case S. ntab already linked! = Nil & N. ID = S. ntab. Self (). ID: // The object created is not your own return errself case S. netrestrict! = Nil &&! S. netrestrict. Contains (N. IP): // network restriction. The IP address of the recipient is not in the whitelist. Return errnotwhitelisted case S. Hist. Contains (N. ID): // This ID has been linked. Return errrecentlydialed} return nil}

Taskdone method. This method will be called after the task is completed. View the task type. If it is a link task, add it to hist. And deleted from the queue that is being linked. For query tasks. Put the query records in lookupbuf.

func (s *dialstate) taskDone(t task, now time.Time) {    switch t := t.(type) {    case *dialTask:        s.hist.add(t.dest.ID, now.Add(dialHistoryExpiration))        delete(s.dialing, t.dest.ID)    case *discoverTask:        s.lookupRunning = false        s.lookupBuf = append(s.lookupBuf, t.results...)    }}

The dialtask. Do method has different do methods for different tasks. Dailtask is mainly responsible for establishing links. If T. DEST does not have an IP address. Then try to query the IP address through resolve. Call the dial method to create a link. For static nodes. If the first failure occurs, resolve the static node again. Then try dial (because the IP address of the static node is configured. If the IP address of the static node changes. Then we try to resolve the new address of the static node and then call the link .)

func (t *dialTask) Do(srv *Server) {    if t.dest.Incomplete() {        if !t.resolve(srv) {            return        }    }    success := t.dial(srv, t.dest)    // Try resolving the ID of static nodes if dialing failed.    if !success && t.flags&staticDialedConn != 0 {        if t.resolve(srv) {            t.dial(srv, t.dest)        }    }}

Resolve Method. This method mainly calls the Resolve Method of the discover network. If it fails, try again

// resolve attempts to find the current endpoint for the destination// using discovery.//// Resolve operations are throttled with backoff to avoid flooding the// discovery network with useless queries for nodes that don‘t exist.// The backoff delay resets when the node is found.func (t *dialTask) resolve(srv *Server) bool {    if srv.ntab == nil {        log.Debug("Can‘t resolve node", "id", t.dest.ID, "err", "discovery is disabled")        return false    }    if t.resolveDelay == 0 {        t.resolveDelay = initialResolveDelay    }    if time.Since(t.lastResolved) < t.resolveDelay {        return false    }    resolved := srv.ntab.Resolve(t.dest.ID)    t.lastResolved = time.Now()    if resolved == nil {        t.resolveDelay *= 2        if t.resolveDelay > maxResolveDelay {            t.resolveDelay = maxResolveDelay        }        log.Debug("Resolving node failed", "id", t.dest.ID, "newdelay", t.resolveDelay)        return false    }    // The node was found.    t.resolveDelay = initialResolveDelay    t.dest = resolved    log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)})    return true}

Dial method, which performs the actual network connection operation. This method is mainly implemented through the SRV. setupconn method, which will be analyzed later when server. Go is analyzed.

// dial performs the actual connection attempt.func (t *dialTask) dial(srv *Server, dest *discover.Node) bool {    fd, err := srv.Dialer.Dial(dest)    if err != nil {        log.Trace("Dial error", "task", t, "err", err)        return false    }    mfd := newMeteredConn(fd, false)    srv.SetupConn(mfd, t.flags, dest)    return true}

Do methods of discovertask and waitexpiretask,

func (t *discoverTask) Do(srv *Server) {    // newTasks generates a lookup task whenever dynamic dials are    // necessary. Lookups need to take some time, otherwise the    // event loop spins too fast.    next := srv.lastLookup.Add(lookupInterval)    if now := time.Now(); now.Before(next) {        time.Sleep(next.Sub(now))    }    srv.lastLookup = time.Now()    var target discover.NodeID    rand.Read(target[:])    t.results = srv.ntab.Lookup(target)}func (t waitExpireTask) Do(*Server) {    time.Sleep(t.Duration)}

Blockchain tutorials ethereum source code analysis p2p-dial.go source code analysis

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.