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.
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...
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...
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...
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-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 ...
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 ...
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...
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...
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...
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...