Ethreum geth p2p RLPx 握手通信过程源码解读 一

目录

一,主进程逻辑

二,连接过程

2.1 主动连接

2.2 被动连接

2.3 连接,

秘钥握手:

协议握手:


Go语言大量使用线程并发,连接的过程中也是协议主进程和多个线程进行并发交互通信的过程。

一,主进程逻辑

p2p层的通信建立连接分为两种,主动拨号建立连接和被动监听连接:

主动拨号连接通过setupDialScheduler()实现,主动的从候选节点集中选择节点进行拨号连接。

P2p/server.go/func (srv *Server) setupDialScheduler(){}

被动监听连接通过listenLoop()循环进行监听,接收连接然后进行相应。

P2p/server.go/func (srv *Server) listenLoop()
ListenLoop()被p2p/server.go 中 func (srv *Server) setupListening() 函数调用,确定参数并启动监听循环。

两监听过程函数被p2p/server.go中 func (srv *Server) Start() (err error)函数调用,start函数是p2p所有协议的启动入口。函数如下:

func (srv *Server) Start() (err error) {
	srv.lock.Lock()
	defer srv.lock.Unlock()
    //互斥锁,避免争用
	if srv.running {
		return errors.New("server already running")
	}
	srv.running = true
	srv.log = srv.Config.Logger
	if srv.log == nil {
		srv.log = log.Root()
	}
	if srv.clock == nil {
		srv.clock = mclock.System{}
	}
	if srv.NoDial && srv.ListenAddr == "" {
		srv.log.Warn("P2P server will be useless, neither dialing nor listening")
	}

	// static fields
	if srv.PrivateKey == nil {
		return errors.New("Server.PrivateKey must be set to a non-nil key")
	}
	if srv.newTransport == nil {
		srv.newTransport = newRLPX
	}
	if srv.listenFunc == nil {
		srv.listenFunc = net.Listen
	}
	srv.quit = make(chan struct{})
	srv.delpeer = make(chan peerDrop)
	srv.checkpointPostHandshake = make(chan *conn)
	srv.checkpointAddPeer = make(chan *conn)
	srv.addtrusted = make(chan *enode.Node)
	srv.removetrusted = make(chan *enode.Node)
	srv.peerOp = make(chan peerOpFunc)
	srv.peerOpDone = make(chan struct{})
    //上面是各种参数设置和通道初始化,由于有大量的线程间通信,使用了大量的通道
    //启动节点自身
	if err := srv.setupLocalNode(); err != nil {
		return err
	}
     //启用监听连接过程
	if srv.ListenAddr != "" {
		if err := srv.setupListening(); err != nil {
			return err
		}
	}
    //启用UDP层节点发现
	if err := srv.setupDiscovery(); err != nil {
		return err
	}
    //启动主动拨号连接过程
	srv.setupDialScheduler()

	srv.loopWG.Add(1)
    //上三过程都是并发执行的,通过run(),一个死循环函数表示协议正在运行与三个线程通过channel进行交互
	go srv.run()
	return nil
}

run()函数是真正的交互函数,首先启动一个死循环等待,然后通过通道进行线程间交互

// run is the main loop of the server.
func (srv *Server) run() {
	srv.log.Info("Started P2P networking", "self", srv.localnode.Node().URLv4())
	defer srv.loopWG.Done()
	defer srv.nodedb.Close()
	defer srv.discmix.Close()
	defer srv.dialsched.stop()
    //defer 关键字,是之后语句在run函数结束返回前执行

	var (
		peers        = make(map[enode.ID]*Peer)
		inboundCount = 0
		trusted      = make(map[enode.ID]bool, len(srv.TrustedNodes))
	)
	// 将可信节点放入映射中以加速检查。
    //信任节点在启动时加载或通过AddTrustedPeer RPC添加
	for _, n := range srv.TrustedNodes {
		trusted[n.ID()] = true
	}

running:
	for {//死循环部分,与其余并发线程进行交互
		select {//select 有case下的可用通道触发,哪个通道读/取 语句可用就执行哪句。等待相应通道的触发
		case <-srv.quit:
			// The server was stopped. Run the cleanup logic.
			break running

		case n := <-srv.addtrusted:
			// AddTrustedPeer使用此通道添加节点到可信节点集。console交互命令addtrustedt添加可信节点,可信节点不受maxpeer等数量限制,依旧可以接入
            //
			srv.log.Trace("Adding trusted node", "node", n)
			trusted[n.ID()] = true
			if p, ok := peers[n.ID()]; ok {
				p.rw.set(trustedConn, true)
			}

		case n := <-srv.removetrusted:
			//该通道被RemoveTrustedPeer用于移除节点从可信节点集。
            //
			srv.log.Trace("Removing trusted node", "node", n)
			delete(trusted, n.ID())
			if p, ok := peers[n.ID()]; ok {
				p.rw.set(trustedConn, false)
			}

		case op := <-srv.peerOp:
			// This channel is used by Peers and PeerCount.
			op(peers)
			srv.peerOpDone <- struct{}{}

		case c := <-srv.checkpointPostHandshake: //通过了encryption handshake
			//一个连接已经通过了加密握手,知道远程节点的身份,同时生成了共享秘钥
            //远程身份是已知的(但尚未验证)。
			if trusted[c.node.ID()] {
				// Ensure that the trusted flag is set before checking against MaxPeers.
				c.flags |= trustedConn
			}
			// TODO: track in-progress inbound node IDs (pre-Peer) to avoid dialing them.
			//校验是否为入站节点,和数量限制,避免重复拨号
			c.cont <- srv.postHandshakeChecks(peers, inboundCount, c)

		case c := <
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值