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.
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值