Go学习之go-ethereum【以太坊】源码分析 - discover-udp(四)

在上一篇《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


猜你喜欢

转载自blog.csdn.net/han0373/article/details/80613337