当TCP连接池跟一个节点进行rlpx握手,建立加密连接后,接下来会触发server.run 函数的addpeer管道传递消息,在server.run()函数里面捕获这个消息,进而创建一个peer实例,创建一个协程单独处理这个节点,进行业务层面的后续处理。
func (srv *Server) run(dialstate dialer) {
//连接池管理协程,负责维护TCP连接列表
//在协程里面运行, 开始监听各个信号量,处理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)
//创建协程 去处理,增加这个peer
go srv.runPeer(p)
peers[c.id] = p
if p.Inbound() {
inboundCount++
}
}
建立连接握手完成以后,调用newPeer()函数新建一个peer,新建peer的时候,通过matchProtocols函数找出匹配的子协议。
连接建立以后,通过 srv.runPeer来处理连接消息。
func (srv *Server) runPeer(p *Peer) {
//个peer连接建立完成后,调用这里来启动一个peer的维护,监听工作
if srv.newPeerHook != nil {
srv.newPeerHook(p)
}
// broadcast peer add
srv.peerFeed.Send(&PeerEvent{
Type: PeerEventTypeAdd,
Peer: p.ID(),
})
// peer处理函数,启动,开始进行消息读取,以及协议方面的处理,通知对应的协议层,增加了一个连接了
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}
}
peer处理函数,启动,开始进行消息读取,以及协议方面的处理,通知对应的协议层,增加了一个连接了。Peer.run函数承担了启动对一个节点的读写循环。
当P2P模块对一个节点握手协议处理完后,addpeer队列接收到,然后调用go srv.runPeer§ 创建协程,最后调用这里, 在协里面维护本节点对于其他节点的链接。怎么维护呢:建立一个事件读写协程,以及管理协程。
func (p *Peer) run() (remoteRequested bool, err error) {
//当P2P模块对一个节点握手协议处理完后,addpeer队列接收到,然后调用srv.runPeer(p) 创建协程,最后调用这里。
//在协里面维护本节点对于其他节点的链接。怎么维护呢:建立一个事件读写协程,以及管理协程。
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{}{}
//子协议启动
p.startProtocols(writeStart, writeErr)
p.readLoop()协程主要不断的进行消息的读取:
func (p *Peer) readLoop(errc chan<- error) {
defer p.wg.Done()
for {
//阻塞读取消息
msg, err := p.rw.ReadMsg()
if err != nil {
errc <- err
return
}
msg.ReceivedAt = time.Now()
//消息处理
if err = p.handle(msg); err != nil {
errc <- err
return
}
}
}
p.rw.ReadMsg()是调用rlpx.go里面的ReadMsg()来进行消息的读取。
读取消息之后,通过handle()函数来进行消息的不同类型的相应的处理。
func (p *Peer) handle(msg Msg) error {
//根据消息Code处理,简单消息直接处理,应用层消息放入对应的协议的in管道里面
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:
//通过偏移量offset和长度Length取出对应协议对应msg.Code
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
}
P2P.Peer模块完成了对一个刚刚建立rlpx加密协议的链接的数据读取逻辑,最后将读取到的消息通过proto.in <- msg; 放入管道里,交给对应的协议的代码逻辑进行处理。
每个子协议启动之后都会调用p.rw.ReadMsg()阻塞等待消息,而ReadMsg()实际读取为:
p2p/peer.go中的ReadMsg()函数
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
}
}
那子协议是如果启动的呢?上面在Peer.run()函数中,除了需要通过协程readLoop()接受消息,还建立了协程startProtocols()来启动子协议。
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() {
//调用协议对应的Run()函数
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()
}()
}
}
每个子协议都有Run()函数,通过Run()函数在阻塞的读取数据。