目录
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 := <