libp2p服务发现之 Multicast DNS(mDNS)

libp2p服务发现之 Multicast DNS(mDNS)

libp2p会在初始化之后不停地通过各种方式“结识”更多的节点,包括利用Bootstrap list、mDNS、DHT等
libP2P定义了routing 接口,目前有2个实现,分别是KAD routing 和 MDNS routing, 扩展很容易, 只要按照接口实现相应的方法即可。本文重点讲解一下mDNS。

在libp2p中,mDNS和Kademlia DHT都是用于节点发现和连接管理的技术。当一个节点需要加入libp2p网络时,它可以通过mDNS和Kademlia DHT查找和连接其他节点,以建立对等网络。具体来说,mDNS通常用于在局域网中查找其他节点,而Kademlia DHT则是在广域网中查找其他节点。

一、Multicast DNS(mDNS)

multicast DNS ,规范文档地址: http://www.ietf.org/rfc/rfc6762.txt

mDNS是Apple公司的Bonjour协议基础上发展起来的, 最终成为一个正式标准(RFC 6762), 从Windows 10, Mac, Linux到RaspberryPi都支持这个协议

Multicast DNS(mDNS)是一种在小型网络(例如单个子网)中无需中央站点分配名称就可以解析网络主机名的方法。它是零配置网络(Zeroconf)协议套件的一部分,该套件旨在让网络设备在没有额外配置的情况下互相发现和通信。

mDNS 的工作原理

mDNS工作原理
首先,在 IP 协议里规定了一些保留地址,其中有一个是 224.0.0.251,对应的 IPv6 地址是 [FF02::FB]。
mDNS 协议规定了一个端口,5353。
mDNS 基于 UDP 协议。
每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁,和我的IP地址是多少。然后其他也有该服务的主机就会响应,也会告诉你,它是谁,它的IP地址是多少。

mDNS的工作原理如下:

当一个设备想要解析一个主机名时,它将会在本地网络中广播一个mDNS请求,请求包含了想要解析的主机名。

所有收到请求的设备都会检查他们是否对应请求中的主机名。如果一个设备发现它对应了请求中的主机名,它就会响应请求,回复它的IP地址。

请求发起设备收到回复后,就知道了对应主机名的设备的IP地址,然后就可以直接和那个设备通信了。

mDNS主要用在小型网络中,例如家庭网络,或者是单个办公室的网络。在这些网络中,设备的数量不多,而且设备之间又经常需要通信,所以mDNS非常适用。在这些网络中使用mDNS,可以让设备在没有中央站点分配名称的情况下互相发现和通信,大大简化了网络配置。

可以使用mDNS来发现同一局域网中的节点。只要节点启动了mDNS服务,它们就可以互相发现对方,然后就可以开始通信了。

Multicast DNS (mDNS) 和 DNS (Domain Name System) 区别

Multicast DNS (mDNS) 和 DNS (Domain Name System) 都是网络中用于解析主机名到 IP 地址的服务。

  1. 服务范围
    DNS 服务是互联网的核心部分,它在全球范围内解析主机名到 IP 地址。而 mDNS 主要用于单个的局域网,如家庭或小型办公室网络。

  2. 中心化 vs 分布式
    DNS 是中心化的,它依赖于分布在全球的 DNS 服务器。当你想要解析一个主机名时,你的设备会向 DNS 服务器发送请求,然后 DNS 服务器回复对应的 IP 地址。而 mDNS 是分布式的,它不依赖于任何中心服务器。当一个设备想要解析主机名时,它会在局域网中广播请求,然后拥有对应主机名的设备会响应请求。

  3. 配置
    DNS 通常需要手动配置,例如你需要购买域名,然后将域名和你的 IP 地址关联。而 mDNS 是零配置的,设备可以自动地在网络中广播其主机名和 IP 地址。

  4. 安全性
    DNS 有一套复杂的安全机制,例如 DNSSEC,来保护 DNS 请求和回复的安全。而 mDNS 的安全性较弱,它主要依赖于网络的物理隔离来保证安全。

二、mDNS和libp2p的关系

Libp2p是一个模块化的网络堆栈,它允许你选择你需要的功能,包括传输、多路复用、加密、对等发现等。对等发现(Peer Discovery)是Libp2p的一个重要部分,它有多种实现方式,mDNS只是其中之一。

Libp2p的mDNS发现模块允许在同一本地网络中的对等节点相互发现,但Libp2p也支持其他的对等发现方法,例如Kademlia Distributed Hash Table(DHT)和Bootstrap List等。这意味着你可以在不使用mDNS的情况下使用Libp2p的服务发现机制。

如果你的应用需要在更大的网络范围内(例如互联网)进行对等节点发现,或者你的网络环境不支持mDNS,你可以选择使用Libp2p的其他服务发现模块。

mDNS是libp2p协议族中的一员,它在构建分布式应用和服务发现方面起着重要的作用。在libp2p中,mDNS用于在同一局域网中的节点之间进行服务发现。它允许节点广播自己的存在,并发现其他节点,这对于创建局域网内的点对点应用非常有用。

三、Kademlia Distributed Hash Table(DHT)和mDNS

mDNS主要用于在局域网(例如家庭网络或小型办公网络)中发现服务。在这种环境中,设备数量相对较少,且都在同一网络中,因此mDNS可以快速有效地发现服务。然而,mDNS不适合用于大型网络或互联网,因为它的工作原理是广播服务信息,如果网络中的设备数量太多,将会导致大量的网络流量。

相比之下,Kademlia DHT则适合于大型网络和互联网。它是一个分布式的键值存储系统,每个节点都存储一部分的数据,并且知道如何找到其它节点存储的数据。这使得Kademlia DHT可以在大规模网络中高效地发现服务,且不会产生过多的网络流量。然而,Kademlia DHT的缺点是它的复杂性较高,需要更多的计算资源。

三、mdns示例

官方代码:https://github.com/libp2p/go-libp2p-examples/tree/master/chat-with-mdns

libp2p中使用mDNS

官方demo:chat-with-mdns

P2P网络编程-3-案例实践:PubSub
参考URL: https://blog.csdn.net/weixin_43988498/article/details/120488890

mDNS作为标准,几乎在任何最新设备上都有实现,掌握mDNS的消息格式开发应用中具有非常重要的意义.

在Go中使用这两种服务发现协议,libp2p提供了相应的模块。下面是一些简单的使用示例:


	// To construct a simple host with all the default settings, just use `New`
	h, err := libp2p.New()
	if err != nil {
		panic(err)
	}
	defer h.Close()

	fmt.Printf("Hello World, my p2p hosts ID is %s\n", h.ID())

	// 设置mDNS服务
	if common.EnableMdns {
		fmt.Printf("hosts ID is %s enable MDNS for discory node!\n", h.ID())
		peerChan := p2p.InitMDNS(h, common.RendezvousString)
		for { // allows multiple peers to join
			peer := <-peerChan // will block until we discover a peer
			//fmt.Println("Found peer:", peer, ", connecting")
			fmt.Println("Found peer:")
			fmt.Println("ID:", peer.ID)
			fmt.Println("Address:", peer.Addrs[0].String())
			for _, addr := range peer.Addrs {
				fmt.Println("Address:", addr.String())
			}

		}
		// todo other
	}

总结:你会发现,它会打印很多地址,都是接口的各种地址,说明他会尝试各种ip发现。

我们可以创建的host,指定监听的端口解决这个问题,如:

h, err := libp2p.New(
	libp2p.ListenAddrStrings(
		"/ip4/0.0.0.0/tcp/9000",      // regular tcp connections
		"/ip4/0.0.0.0/udp/9000/quic", // a UDP endpoint for the QUIC transport
	),
)

0.0.0.0 可以改为具体某个接口的ip,这样找到的都是可以这个ip联通的局域网中的节点,地址列表就比较简单清晰。

使用Kademlia DHT进行服务发现:

import (
    "github.com/libp2p/go-libp2p"
    "github.com/libp2p/go-libp2p-core/host"
    "github.com/libp2p/go-libp2p-kad-dht"
)

h, _ := libp2p.New()
dht, _ := dht.New(ctx, h)
自定义 Notifee 结构

type discoveryNotifee struct {
PeerChan chan peer.AddrInfo
} 和 type discoveryNotifee struct {
h host.Host
}
有什么区别

这两个类型discoveryNotifee的区别在于它们内部所包含的字段。

第一个类型discoveryNotifee包含一个字段PeerChan,它是一个chan peer.AddrInfo类型。这个通道(channel)用于接收peer.AddrInfo结构体,它可能包含有关发现到的对等节点的信息。通过读取PeerChan通道,可以获取到发现到的对等节点的地址信息。

第二个类型discoveryNotifee包含一个字段h,它是一个host.Host类型。host.Host表示节点的主机接口,它提供了与其他节点进行通信的功能。通过这个字段,可以访问节点的主机对象,从而进行更多的操作,如建立连接、发送消息等。

总的来说,这两个类型的区别在于它们内部字段的不同。第一个类型使用通道来接收发现到的对等节点的地址信息,第二个类型则将主机对象作为字段,提供更多的节点通信功能。具体使用哪个类型取决于代码的需求和设计。

type Notifee interface {
HandlePeerFound(peer.AddrInfo)
}
官方Notifee 接口只规定要实现的方法,我们自定义的 xxxNotifee 结构体,我们根据需要自由发挥。

猜你喜欢

转载自blog.csdn.net/inthat/article/details/134536583