從handle(peer)倒推,尋找被調用關係
在p2p.Server.run()
函數,無限for
循環中,執行select
中case c := <-srv.addpeer:
分支的操作,進行handshake
,並且goroutine
啓動Server.runPeer()
,並進一步調用peer.run()
,最終在startProtocols()
中調用proto.Run(p,rw)
,該Run()函數爲handler.go
中NewProtocolManager()
中的函數類型變量Run
,該變量最終調用handle(peer)
,最終進行peer的其他操作。
node.Start() >> server.Start() >> Server.run() >> go, Server.runPeer() >> peer.run() >> startProtocols() >> proto.Run(p,rw)
p2p
中的Server
的成員變量addpeer
爲conn
類型的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
的成員變量,包含addpeer
、addstatic
等各種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
,該channel
是p2p.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
循環中,執行select
中case 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
生成的task
爲dialTask
,所以在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.
}