在上一篇《Kademlia协议介绍》中对其原理进行了简单的阐述,接着,就从基于此协议实现的upd的数据结构进行继续分析。
- 什么是UDP
- 以太坊p2p中的udp结构定义
- upd的创建
源码中的目录结构:
1、UDP
用户数据报协议(User Datagram Protocol,缩写为UDP),又称用户数据报文协议,是一个简单的面向数据报(package-oriented)的传输层协议,正式规范为 RFC 768。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。因此,也被称为不可靠数据报协议。2、udp中的常量定义
(1)错误信息
(2)超时信息
(3)网络传输的4种数据包类
(4)报文格式
// RPC request structures
type (
ping struct {
Version uint //协议版本
From, To rpcEndpoint //IP源/目标 地址
Expiration uint64 //超时时间
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"` //可忽略的字段(兼容)
}
// pong is the reply to ping.(回应ping)
pong struct {
To rpcEndpoint //目标IP
ReplyTok []byte // ping哈希
Expiration uint64 // 绝对超时时间
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"` //(同上)
}
// 查询距离target较近的节点
findnode struct {
Target NodeID // doesn't need to be an actual public key
Expiration uint64
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"`
}
// reply to findnode(附近节点,回应findnode)
neighbors struct {
Nodes []rpcNode //节点信息(附近)
Expiration uint64
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"` //(同上)
}
//rpc节点
rpcNode struct {
IP net.IP // len 4 for IPv4 or 16 for IPv6
UDP uint16 // for discovery protocol
TCP uint16 // for RLPx protocol
ID NodeID
}
rpcEndpoint struct {
IP net.IP // len 4 for IPv4 or 16 for IPv6
UDP uint16 // for discovery protocol
TCP uint16 // for RLPx protocol
}
)
(5)udp结构
(6)table的相关配置
// Config holds Table-related settings.
type Config struct {
// These settings are required and configure the UDP listener:
PrivateKey *ecdsa.PrivateKey
// These settings are optional:
AnnounceAddr *net.UDPAddr // DHT的本地地址
NodeDBPath string // 数据节点存储的文件路径
NetRestrict *netutil.Netlist // 网络连接
Bootnodes []*Node // 存放"推荐"节点
Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
}
(7)pending、reply结构
在协议的实现中,我们希望findnode能发送1个或1个以上的pending,一般而言,neighbor(附近节点)不能够与特定的节点分组相匹配。所以,以太坊的实现方式是,通过多个挂起的pending(挂起),从而reply时存储的一个callback函数来进行实现。从一个节点来的所有数据包都会分配到这个节点对应的callback上面。
pending:
reply:
readpacket:
3、创建UDP
(1)监听udp返回tab
(2)创建udp、关闭udp
func newUDP(c conn, cfg Config) (*Table, *udp, error) {
udp := &udp{
conn: c,
priv: cfg.PrivateKey,
netrestrict: cfg.NetRestrict,
closing: make(chan struct{}),
gotreply: make(chan reply),
addpending: make(chan *pending),
}
realaddr := c.LocalAddr().(*net.UDPAddr)
if cfg.AnnounceAddr != nil { //本地地址不为空则直接返回使用
realaddr = cfg.AnnounceAddr
}
//分发一个TCP端口
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
//创建table,与table.go相关
tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes)
if err != nil {
return nil, nil, err
}
udp.Table = tab
go udp.loop()//go routine 保持pending reply
go udp.readLoop(cfg.Unhandled)//读取网络数据
return udp.Table, udp, nil
}
//所有loop结束关闭udp的操作
func (t *udp) close() {
close(t.closing)
t.conn.Close()
}
-----------------------------------------------
本文介绍了upd的基本结构,下一章将对udp的执行过程进行具体分析,文章部分内容来自ZtesoftCS的github,在此鸣谢。
有任何建议或问题,欢迎加微信一起学习交流,欢迎从事IT,热爱IT,喜欢深挖源代码的行业大牛加入,一起探讨。
个人微信号:bboyHan