p2p内部实现

  • 运行p2p服务

func (srv *Server) run(dialstate dialer) {

// 准备临时变量

// 根据svr.TrustedNodes,在trusted中标记为信任

// 定义 删除任务、开始任务、安排任务 函数变量

for无限循环 {

// 执行 安排任务

select 条件判断:

:如果收到退出消息 {

// 跳出for无限循环

}

:如果收到添加静态节点消息 {

// 调用dialer.addStatic接口,将节点信息添加到静态节点信息map中

}

:如果收到移除静态节点消息 {

// 调用dialer.removeStatic接口

// 如果节点已经连接成功,则得到peer对象,断开连接

}

:如果收到获取peer信息的消息 {

// 调用收到的回调函数,将当前peers作为参数传入

}

:如果收到任务完成消息 {

// 调用dialer.taskDone函数,更新dialer状态(拨号任务:在拨号间隔时间保护对象中,添加当前完成节点信息,从正在拨号节点信息记录中删除当前完成节点信息;discover任务:lookupRunning置为false,查找结果追加到lookupBuf中)

// 调用本地delTask函数,从本地runningTasks中删除

}

:如果收到加密握手成功消息 {

// 验证加密握手(如果非信任非静态非storeman,而且已连接peers数量达到MaxPeers,则返回节点过多错误;如果peers包含当前握手节点,则返回以存在错误;如果是自身,则返回自身节点错误fuze返回nil)

// 将验证结果发送给conn

}

:如果收到协议握手成功消息 {

// 验证协议握手(远端协议与本地支持协议有匹配,并且加密握手验证通过)

// 如果验证通过,则根据conn信息创建peer实例,添加到peers中,创建新协程运行server.runPeer(),激活新peer

// 验证结果发送给conn

}

:如果收到peer断开连接消息 {

// 将node从peers中删除

}

}

// 结束收尾

// 关闭discover

// 关闭discoverV5

// 逐个peers断开连接

// 等待所有peers断开连接回应

}

  • 运行peer

func (p *Peer) run() (remoteRequested bool, err error) {

// 准备局部变量

// 启动新协程,使用for无限循环读取peer网络消息(peer.readLoop)

// 启动新协程,使用for无限循环+定时器,向peer远端发送心跳消息(peer.pingLoop)

// 启动所有协议handlers???

for无限循环 {

select条件判断:

: 如果收到网络发送完成消息 {

// 则再设置一个网络发送消息到writeStart

}

: 如果收到一个网络接收错误消息 {

// 处理错误,退出for无限循环

}

: 如果收到一个协议错误消息 {

// 处理错误,退出for无限循环

}

: 如果收到一个断开连接消息 {

// 退出for无限循环

}

}

// 结尾处理

}

  • 创建p2p任务

func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {

// 记录s的起始时间

// 记录配置的对大动态拨号个数()

needDynDials := s.maxDynDials // (srv.MaxPeers + 1) / 2

// 统计peers(已连接)中标记动态拨号的个数,从needDynDials中减去

// 统计正在拨号的节点个数,从needDynDials中减去

// 从拨号历史记录中,删除已经过期的记录

// 从静态节点拨号任务信息中查找可用信息(没有正在拨号中、没有对应的peer对象、不是自身节点、如果配置白名单但是不在白名单内、不在拨号时间间隔保护期内),添加到newtasks中,并从needDynDials减去名额

// 从storeman节点拨号任务信息中查找可用信息(没有正在拨号中、没有对应的peer对象、不是自身节点、如果配置白名单但是不在白名单内、不在拨号时间间隔保护期内),添加到newtasks中,并从needDynDials减去名额

// 如果已连接成功的节点(peers)数为0,并且设置的boostnode不为空,并且尚有剩余动态拨号名额(needDynDials),并且s启动时间到现在已经超过fallback期限,则添加boostnodes第一个节点拨号任务到newtasks,同时将第一个boostnodes节点信息移至最后一位,并从needDynDials减去名额

// 剩余动态拨号任务名额数除以2,为随机候选人名额数(randomCandidates)

// 从自动查询器查询到的节点信息中,随机选取randomCandidates个,添加到newtasks,并从needDynDials减去名额

// 把lookupBuf中的节点添加到newtasks,needDynDials减去名额,并且从lookupBuf中清除

// 如果lookupBuf中的数据量不足,并且s.loopupRunning为false,则添加一个discoverTask到newtasks,并且设置s.loopupRunning为true

// 如果正在运行的task数量不为0,并且newtasks长度为0,但是保护期历史记录信息个数不为0,则在newtasks中添加一个等待超时任务(waitExpireTask)

// 返回newtasks

}

  • 拨号任务执行过程

func (t *dialTask) Do(srv *Server) {

// 检测目标node的信息是否完整(是否有ip)

// 如果不完整,则调用dialTask.resolve来确定node的ip(内部依赖discoverTable.Resolve,通过网络向其他节点查询目标node的ip信息)

// 调用dialTask.dial函数进行拨号连接

// 如果拨号失败,并且node是静态node或storeman node,则尝试一次重新resolve和拨号

}

  • 拨号任务执行过程

func (t *dialTask) dial(srv *Server, dest *discover.Node) bool {

// 根据node信息,建立网络连接

// 根据连接对象,创建可计量连接对象

// 调用server.SetupConn接口,与远端node进行handshake,并且新增一个peer对象

}

  • handshake过程

func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) {

// RLPx 加密握手(encryption handshake,依赖rlpx.doEncHandshake)

// 向p2p.server发送加密握手成功消息,等待返回结果

// RLPx协议握手(protocol handshake,依赖rlpx.doProtoHandshake)

// 向p2p.server发送协议握手成功消息,等待返回结果

}

  • 接收外部主动连接的peer

func (srv *Server) listenLoop() {

// 计算同时最多接收连接的个数

// 创建连接个数控制器,通过chan控制接收连接的个数

for 无限循环 {

// 消耗一个连接个数名额

for 无限循环 {

// 调用accept接受外部连接

// 如果收到有效连接,则跳出无限循环

}

// 如果设置了网络受限,则检测远端ip是否在白名单内

// 根据新的连接对象,创建一个计量连接对象

// 创建新协程,在其内调用server.SetupConn端口,先进行handshake,然后新增一个新的peer对象

}

}

  • p2p网络消息分发到storeman

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

}

}

}

func (p *Peer) handle(msg Msg) error {

......

select {

case proto.in <- msg:

return nil

case <-p.closed:

return io.EOF

}

}

return nil

}

func (sm *Storeman) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {

......

for {

// fetch the next packet

packet, err := rw.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

}

}

  • storeman消息驱动的启动

func New(cfg *Config, accountManager *accounts.Manager, aKID, secretKey, region string) *Storeman {

storeman := &Storeman{

peers: make(map[discover.NodeID]*Peer),

quit: make(chan struct{}),

cfg: cfg,

}

......

storeman.protocol = p2p.Protocol{

Name: mpcprotocol.ProtocolName,

Version: uint(mpcprotocol.ProtocolVersion),

Length: mpcprotocol.NumberOfMessageCodes,

Run: storeman.HandlePeer,

NodeInfo: func() interface{} {

return map[string]interface{}{

"version": mpcprotocol.ProtocolVersionStr,

}

},

}

return storeman

}

func (n *Node) Start() error {

......

// Gather the protocols and start the freshly assembled P2P server

for _, service := range services {

running.Protocols = append(running.Protocols, service.Protocols()...)

}

if err := running.Start(); err != nil {

return convertFileLockError(err)

}

......

}

func (sm *Storeman) Protocols() []p2p.Protocol {

return []p2p.Protocol{sm.protocol}

}

func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) {

p.wg.Add(len(p.running))

for _, proto := range p.running {

......

go func() {

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()

}()

}

}

完毕

猜你喜欢

转载自blog.csdn.net/bjzhaoxiao/article/details/81251720
P2P