文章目录
- [libp2p服务发现之 Multicast DNS(mDNS)](#libp2p服务发现之 Multicast DNS(mDNS))
-
- [一、Multicast DNS(mDNS)](#一、Multicast DNS(mDNS))
-
- [mDNS 的工作原理](#mDNS 的工作原理)
- [Multicast DNS (mDNS) 和 DNS (Domain Name System) 区别](#Multicast DNS (mDNS) 和 DNS (Domain Name System) 区别)
- 二、mDNS和libp2p的关系
- [三、Kademlia Distributed Hash Table(DHT)和mDNS](#三、Kademlia Distributed Hash Table(DHT)和mDNS)
- 三、mdns示例
-
- libp2p中使用mDNS
-
- [自定义 Notifee 结构](#自定义 Notifee 结构)
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 地址的服务。
-
服务范围
DNS 服务是互联网的核心部分,它在全球范围内解析主机名到 IP 地址。而 mDNS 主要用于单个的局域网,如家庭或小型办公室网络。
-
中心化 vs 分布式
DNS 是中心化的,它依赖于分布在全球的 DNS 服务器。当你想要解析一个主机名时,你的设备会向 DNS 服务器发送请求,然后 DNS 服务器回复对应的 IP 地址。而 mDNS 是分布式的,它不依赖于任何中心服务器。当一个设备想要解析主机名时,它会在局域网中广播请求,然后拥有对应主机名的设备会响应请求。
-
配置
DNS 通常需要手动配置,例如你需要购买域名,然后将域名和你的 IP 地址关联。而 mDNS 是零配置的,设备可以自动地在网络中广播其主机名和 IP 地址。
-
安全性
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提供了相应的模块。下面是一些简单的使用示例:
golang
// 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,指定监听的端口解决这个问题,如:
go
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进行服务发现:
golang
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 结构体,我们根据需要自由发挥。