addPeer操作解析

從handle(peer)倒推,尋找被調用關係

p2p.Server.run()函數,無限for循環中,執行selectcase c := <-srv.addpeer:分支的操作,進行handshake,並且goroutine啓動Server.runPeer(),並進一步調用peer.run(),最終在startProtocols()中調用proto.Run(p,rw),該Run()函數爲handler.goNewProtocolManager()中的函數類型變量Run,該變量最終調用handle(peer),最終進行peer的其他操作。

node.Start() >> server.Start() >> Server.run() >> go, Server.runPeer() >> peer.run() >> startProtocols() >> proto.Run(p,rw)

p2p中的Server的成員變量addpeerconn類型的channel,在Server.checkpoint(c,srv.addpeer)中的select選擇執行srv.addpeer <- c,而checkpoint()函數僅在Server.SetupConn()中被調用,而SetupConn()Server.listenLoop()中被調用,listenLoop()函數中包含我們之前進行admin.addPeer()所用到的enode信息,由server.makeSelf()函數生成

server.Start() >> startListening() >> go,listenLoop() >> SetupConn() >> checkpoint(),`srv.addpeer <- c`

server.Start()函數,首先初始化server的成員變量,包含addpeeraddstatic等各種channel;然後根據server繼承config中的各種XXDiscovery進行各自的操作。

==========================================================================

從admin.addPeer()正推,找調用關係

命令行運行admin.addPeer()實際調用node/api.go中的AddPeer(url),該函數根據url初始化一個p2p/discover.Node,然後調用p2p.server.AddPeer(node),最終返回true

p2p.server.AddPeer(node)僅包含一個select語句,將node發送到srv.addstatic,該channelp2p.Server的一個discover.Node類型的成員變量,定義如下:

// Node represents a host on the network. The fields of Node may not be modified.
type Node struct {
    IP       net.IP // len 4 for IPv4 or 16 for IPv6
    UDP, TCP uint16 // port numbers
    ID       NodeID // the node's public key

    // This is a cached copy of sha3(ID) which is used for node distance calculations.
        // This is part of Node in order to make it possible to write tests that need a node at a certain distance.
    // In those tests, the content of sha will not actually correspond with ID.
    sha common.Hash

    // whether this node is currently being pinged in order to replace it in a bucket
    contested bool
}

p2p.Server.Run()函數,無限for循環中,執行selectcase n := <-srv.addstatic:分支的操作,調用dialstate.addStatic(n),該方法是dialer接口的一個方法,由p2p/dial.go中的dialstate實現,該函數僅執行s.static[n.ID] = &dialTask{flags: staticDialedConn, dest: n}dialTask的結構體如下:

// A dialTask is generated for each node that is dialed. Its fields cannot be accessed while the task is running.
type dialTask struct {
    flags        connFlag
    dest         *discover.Node
    lastResolved time.Time
    resolveDelay time.Duration
}

s.static在同一文件中的newTasks()中被使用,該函數中通過for循環遍歷s.static,然後針對每一個static,調用s.checkDial(t.dest, peers)進行連接檢查,若沒有錯誤產生,則會將static添加到newtasks中,另外,newTasks()函數的返回值爲newtasks.

newTasks()函數在Server.run()中在函數類型變量scheduleTasks中被調用,newTasks()的返回值newtasks隨後作爲startTasks()的參數,startTasks()會根據每個task的類型,goroutine啓動Do(server)函數;而scheduleTasks()在隨後的無限for循環中被調用。

Server.run() >> for, scheduleTasks() >> newTasks() ==> startTasks() >> task.Do()

由於admin.addPeer生成的taskdialTask,所以在startTasks()中調用的Do()函數如下:

func (t *dialTask) Do(srv *Server) {
    if t.dest.Incomplete() { //=>IP爲nil
        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)
        }
    }
}

該函數調用dial()嘗試實際連接,dial()函數如下:

func (t *dialTask) dial(srv *Server, dest *discover.Node) bool {
    fd, err := srv.Dialer.Dial(dest) //=>fd爲net.Conn類型
    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
}

dial()函數調用srv.Dialer.Dial(dest),該方法由func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error){}實現,建立一個TCP連接,其後會調用go語言庫函數,不再深究,如下:

// Dial creates a TCP connection to the node
func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) {
    addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
    return t.Dialer.Dial("tcp", addr.String()) //=> 該函數位於/usr/lib/go/src/net/dial.go,爲go語言庫函數
}

隨後,dial()函數調用srv.SetupConn(mfd, t.flags, dest),如下:

SetupConn runs the handshakes and attempts to add the connection as a peer. It returns when the connection has been added as a peer or the handshakes have failed.

func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) {
    // Prevent leftover pending conns from entering the handshake.
    ...
        初始化handshake連接c,類型爲conn

    // Run the encryption handshake.
        ...
        調用rlpx.go中的doEncHandshake(srv.PrivateKey, dialDest)

    // For dialed connections, check that the remote public key matches.
    ...
        判斷是否匹配

        ...
        調用srv.checkpoint(c, srv.posthandshake),與“從handle(peer)倒推,尋找被調用關係”中的checkpoint()爲同一個函數,
        但是參數不同,srv.posthandshake爲channel,表示conn已通過encHandShake(身份已知,但是尚未驗證)

    // Run the protocol handshake
    ...
        調用c.doProtoHandshake(srv.ourHandshake),該函數會通過p2p.Send(),發送handshakeMsg標識的消息;
        然後,調用readProtocolHandshake()讀取接收到的handshakeMsg標識消息。(該標識和pbftMessage標識類似)

    c.caps, c.name = phs.Caps, phs.Name
    ...
        調用srv.checkpoint(c, srv.addpeer),與“從handle(peer)倒推,尋找被調用關係”中的checkpoint()爲同一個函數,
        參數也完全相同,其後操作見上文。

    // If the checks completed successfully, runPeer has now been launched by run.
}
發佈了53 篇原創文章 · 獲贊 55 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章