简述以太坊 P2P 网络之 UDP
个人认为以太坊是区块链项目中带来技术重新认识和学习的不错的项目,特别是在 P2P 网络这一块。本文将介绍 P2P 网络中负责节点之间的通信连接和服务发现,本文的内容主要是对代码层级的理解,可能存在对其理解的错误,欢迎指点。包括以下两个方面:
种子节点初始化,节点发现
节点连接及相互通信
pending
一种延迟处理逻辑,提供一个回调机制
当某一个操作发起异步请求时,就使用
pending
结构封装一个闭包,当收到异步回复后从 pending 列表取出这个闭包,执行回调,因此在这个回调里可以完成数据包校验等后处理,如 findnode 操作将更新 k 桶的操作暂存,再获取到异步回复后执行这个闭包完成 k 桶更新提供多个回复接收功能,一个 RPC 请求可能会对应多个回复包,比如 findnode 对应多个 neigbours 回复包,此时可以提供多个
pending
进行逐个包校验
种子节点初始化及节点发现
这部分的逻辑的实现主要在p2p/discover/table.go
中,在 udp 中newUDP
方法调用newTable
开始种子节点及节点的发现。
newTable
:执行该函数会传入Bootnodes
信息,配置信息在params/bootnodes.go
中,为初始连接节点,服务启动后就从这些节点开始进行节点的发现和扩散。关键执行方法tab.setFallbackNodes
:验证bootnodes
信息,将节点信息赋值给tab.nursery
tab.seedRand
:设置随机seed
tab.loadSeedNodes
:将bootnodes
加入路由表tab.loop
:创建goroutine
发现节点并进行连接tab.doRefresh
:创建goroutine
刷新所有节点(30 分钟),并进行连接tab.lookup
:查找距离自己最近的节点tab.closest
:获取距离target
最近的16
个节点tab.findnode
:向最近的节点发起findnode
请求,并增加处理neighborsPacket
闭包的pending
,具体实现为udp.go
中的findnode
,对于超过 24 小时没有接收到ping
包的节点重新发送ping
;对端节点接收到findnode
请求后,查找附近的 16 个节点,并发送neighborsPacket
--
findnode-neighbors
时序图
由于 UDP 有最大报文数限制,所以能够发送的邻近节点数目是有限的,需要拆包发送
节点连接及相互通信
P2P 服务的启动位于p2p/server.go
中的Start()
。
建立一个 UDP 连接服务discover.ListenUDP
这个方法的关键实现是初始化三个通道,2 个goroutine
。
三个通道
closing
:关闭 udpgotreply
:消息回复addpending
:pending
消息处理2 个
goroutine
udp.loop()
:主要监听t.gotreply
和t.addpending
t.addpending
:接收并增加一个闭包t.gotreply
:接收回复遍历,执行callback
通知errC
通道udp.readLoop()
:读取 UDP 数据包,执行t.handlePacket
进行数据包处理t.handlePacket
:执行decodePacket
对数据包进行解码,然后调用相应数据包的handle
方法
网络传输四种数据包
数据包类型
数据包结构
ping-pong 时序图
版权声明: 本文为 InfoQ 作者【devpoint】的原创文章。
原文链接:【http://xie.infoq.cn/article/a4248a3ca756e8f7eef4c4219】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论