Ethereum p2p network (six): Ethereum peer connection and message processing

When the TCP connection pool performs a rlpx handshake with a node to establish an encrypted connection, it will trigger the addpeer pipe of the server.run function to pass the message, capture the message in the server.run() function, and create a peer instance to create a The coroutine processes this node separately and performs subsequent processing at the service level.

func (srv *Server) run(dialstate dialer) {
 / / Connection pool management coroutine, responsible for maintaining the list of TCP connections
         / / Run in the coroutine, start listening to each semaphore, deal with the addition, deletion and modification of peer

	...
case c := <-srv.addpeer:
		err := srv.protoHandshakeChecks(peers, inboundCount, c)
		if err == nil {
			p := newPeer(c, srv.Protocols)
			if srv.EnableMsgEvents {
				p.events = &srv.peerFeed
			}
			name := truncateName(c.name)
			srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1)
			 / / Create a coroutine to deal with, add this peer
			go srv.runPeer(p)
			peers[c.id] = p
			if p.Inbound() {
				inboundCount++
			}
		}

After the connection handshake is established, call the newPeer() function to create a new peer. When creating a new peer, find the matching sub-protocol through the matchProtocols function.

After the connection is established, the connection message is processed by srv.runPeer.

func (srv *Server) runPeer(p *Peer) {
	 // After the peer connection is established, call here to start a peer maintenance and monitor work.

	if srv.newPeerHook != nil {
		srv.newPeerHook(p)
	}

	// broadcast peer add
	srv.peerFeed.Send(&PeerEvent{
		Type: PeerEventTypeAdd,
		Peer: p.ID(),
	})

	 / / Peer processing function, start, start message reading, and protocol processing, notify the corresponding protocol layer, added a connection
	remoteRequested, err := p.run()

	// broadcast peer drop
	srv.peerFeed.Send(&PeerEvent{
		Type:  PeerEventTypeDrop,
		Peer:  p.ID(),
		Error: err.Error(),
	})

	srv.delpeer <- peerDrop{p, err, remoteRequested}
}

The peer processing function starts, starts to read the message, and processes the protocol, notifies the corresponding protocol layer, and adds a connection. The Peer.run function takes care of starting a read and write loop on a node.
When the P2P module processes a node handshake protocol, the addpeer queue receives it, then calls go srv.runPeer§ to create a coroutine, and finally calls here, which maintains the node for other nodes. The link to the node. How to maintain it: establish an event read and write coroutine, and manage the coroutine.

func (p *Peer) run() (remoteRequested bool, err error) {
	 //When the P2P module processes a node handshake protocol, the addpeer queue receives it, then calls srv.runPeer(p) to create the coroutine, and finally calls it here.                  
         // Maintain the link of this node to other nodes in the association. How to maintain it: establish an event read and write coroutine, and manage the coroutine.
	var (
		writeStart = make(chan struct{}, 1)
		writeErr   = make(chan error, 1)
		readErr    = make(chan error, 1)
		reason     DiscReason // sent to the peer
	)
	p.wg.Add(2)
	go p.readLoop(readErr)
	go p.pingLoop()

	// Start all protocol handlers.
	writeStart <- struct{}{}
	 //Sub-protocol startup
	p.startProtocols(writeStart, writeErr)

The p.readLoop() coroutine mainly reads messages continuously:

func (p *Peer) readLoop(errc chan<- error) {
	defer p.wg.Done()
	for {
		 / / Block read messages
		msg, err := p.rw.ReadMsg()
		if err != nil {
			errc <- err
			return
		}
		msg.ReceivedAt = time.Now()
		 / / message processing
		if err = p.handle(msg); err != nil {
			errc <- err
			return
		}
	}
}

p.rw.ReadMsg() calls ReadMsg() in rlpx.go to read the message.
After reading the message, the handle() function is used to perform the corresponding processing of different types of messages.

func (p *Peer) handle(msg Msg) error {
/ / According to the message Code processing, simple messages directly processed, application layer messages into the corresponding protocol in the pipeline
	switch {
	case msg.Code == pingMsg:
		msg.Discard()
		go SendItems(p.rw, pongMsg)
	case msg.Code == discMsg:
		var reason [1]DiscReason
		// This is the last message. We don't need to discard or
		// check errors because, the connection will be closed after it.
		rlp.Decode(msg.Payload, &reason)
		return reason[0]
	case msg.Code < baseProtocolLength:
		// ignore other base protocol messages
		return msg.Discard()
	default:
		 / / Get the corresponding protocol corresponding msg.Code by offset offset and length Length
		proto, err := p.getProto(msg.Code)
		if err != nil {
			return fmt.Errorf("msg code out of range: %v", msg.Code)
		}
		select {
		case proto.in <- msg:
			return nil
		case <-p.closed:
			return io.EOF
		}
	}
	return nil
}

The P2P.Peer module completes the data read logic for a link that just established the rlpx encryption protocol.Finally, the message will be read by proto.in <- msg; Put into the pipeline and hand it to the code logic of the corresponding protocol for processing.

After each sub-protocol is started, p.rw.ReadMsg() is called to block the wait message, and ReadMsg() is actually read as:

ReadMsg() function in p2p/peer.go

func (rw *protoRW) ReadMsg() (Msg, error) {
	select {
	case msg := <-rw.in:
		msg.Code -= rw.offset
		return msg, nil
	case <-rw.closed:
		return Msg{}, io.EOF
	}
}

If the sub-protocol is activated? In the Peer.run() function above, in addition to accepting messages through the coroutine readLoop(), a coroutine startProtocols() is also established to start the sub-protocol.

func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) {
	p.wg.Add(len(p.running))
	for _, proto := range p.running {
		proto := proto
		proto.closed = p.closed
		proto.wstart = writeStart
		proto.werr = writeErr
		var rw MsgReadWriter = proto
		if p.events != nil {
			rw = newMsgEventer(rw, p.events, p.ID(), proto.Name)
		}
		p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version))
		go func() {
		 / / Call the corresponding Run () function of the protocol
			err := proto.Run(p, rw)
			if err == nil {
				p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version))
				err = errProtocolReturned
			} else if err != io.EOF {
				p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err)
			}
			p.protoErr <- err
			p.wg.Done()
		}()
	}
}

Each sub-protocol has a Run() function that reads data in a block by the Run() function.

Intelligent Recommendation

Ethereum source code analysis-Ethereum P2P protocol

Create P2P server The code first did some checking work: locking, judging whether the node is already running, checking whether datadir can be opened, and then initializing the P2P server configuratio...

Principle of P2P (Peer to Peer) network

Recently I am studying P2P technology, but there are not many related materials. I figured it out myself, share some principles of learning P2P, and how to create a P2P chat application. P2P here refe...

Ethereum source code analysis of P2P network (3, UDP underlying communication)

Blockchain Special: https://blog.csdn.net/fusan2004/article/details/80879343, welcome to check, original works, please indicate when reprinted! This week's work is a little busy. The development of th...

Ethereum source code P2P network and node discovery mechanism

http://www.cnblogs.com/blockchain/p/7943962.html directory 1 Introduction to distributed network 1.1 Introduction to Kad Network 1.2 Kad network node distance 1.3 K bucket 1.4 Kad communication protoc...

Ethereum source code--Analysis of P2P network source code (2)

ethereum-p2p(2) node discovery mechanism code analysis (v1.8.24) 1. Guide ​ This part mainly analyzes the source code of the Ethereum node discovery mechanism. The Ethereum node discovery part mainly ...

More Recommendation

Ethereum source code--Analysis of P2P network source code (1)

ethereum-p2p code analysis (v1.8.24) This article mainly analyzes the code according to the main logic of p2p 1. Start analyzing the main code 1.1 server.Start() The above is the code when the server ...

Go Ethereum P2P : Implementation [1]

Introduction The code implementation of the P2P part. P2P is a very important component in the Go-ethererum project. All important services are built on it (eth/whisper/swarm). P2P is mainly responsib...

Ethereum p2p Kademlia's implementation of the fourth

I talked about the implementation of p2p network, and now it involves the interaction of application data (blocks, transactions, etc.) between nodes. 1. User protocol interface defined by the p2p modu...

Ethereum p2p Kademlia's implementation of five

This article mainly analyzes the establishment of two tcp connections. Active dial target node Accept connections from other nodes Give important conclusions first: Whether it is passively accepting c...

Ethereum p2p Kademlia's implementation of eight

I have already analyzed the call process of ethereum's tcp communication. Now look at the underlying encoding of tcp communication 1. Handshake process when establishing a connection The completion of...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top