【libp2p——examples——relay分析】

1.例子简介

这个项目演示了使用Go中的libp2p库建立一个基于中继的点对点通信。它的特点是创建不可访问的libp2p主机,并通过中继节点促进它们的通信。

  • 创建两个“不可达”的libp2p主机。
  • 建立中继节点,使这些主机之间能够通信。

2. 步骤

  1. 存在两个不能直接存在的节点Node1,Node2(并且这两个节点接受来自中继节点的入站连接)
  2. 存在一个中继节点Relay
  3. Node1和Node2应该都可以连接到中继节点Relay,并将其加入到自己的地址簿
  4. Node2需要向Relay预定一个端口用来转发自己的消息
  5. Node1通过realy新建一个连接地址/p2p/relayAddress/p2p-circuit/p2p/targetAddress
  6. Node1尝试Connect Node2,成功后会将其加入到targetAddress加入到自己的地址簿
  7. 可以直接用Node2的ID和协议进行通讯

3. 代码分析

删除了一部分错误处理,使代码看起来更加简洁

func run(){
	// 创建一个在NAT后的节点1
	unreachable1, err := libp2p.New(
		libp2p.NoListenAddrs,
		// 启动中继功能是一个默认选项,但是我们在之前的NoListenAddrs覆盖了它,所以需要再次显式调用
		// 该选项仅将libp2p配置为接受来自中继的入站连接,并在远程对等体请求时进行出站连接。
		libp2p.EnableRelay(),
	)
	// 创建一个在NAT后的节点2
	unreachable2, err := libp2p.New(
		libp2p.NoListenAddrs,
		libp2p.EnableRelay(),
	)
	// 构建一个连接节点2的地址
	unreachable2info := peer.AddrInfo{
		ID:    unreachable2.ID(),
		Addrs: unreachable2.Addrs(),
	}
	err = unreachable1.Connect(context.Background(), unreachable2info)
	if err != nil {
		log.Printf("This is normal because NAT2 does not listen to addresses")
		return
	}
	// 正如我们所怀疑的那样,我们无法在无法到达的主机之间直接拨号
	// 创建一个主机作为中间人,代表我们传递消息
	relay1, err := libp2p.New()
	// 配置主机以提供电路中继服务。任何可以在网络(或互联网)中直接拨号的主机都可以提供电路中继服务,这不仅仅是“专用”中继服务的工作。在电路继电器v2(我们在这里使用!)中,它的速率是有限的,因此任何节点都可以安全地提供此服务
	_, err = relay.New(relay1)
	// 获得中继节点的连接地址
	relay1info := peer.AddrInfo{
		ID:    relay1.ID(),
		Addrs: relay1.Addrs(),
	}
	// 将unreachable1和unreachable2都连接到relay1
	if err := unreachable1.Connect(context.Background(), relay1info); err != nil {
		log.Printf("Failed to connect unreachable1 and relay1: %v", err)
		return
	}
	if err := unreachable2.Connect(context.Background(), relay1info); err != nil {
		log.Printf("Failed to connect unreachable2 and relay1: %v", err)
		return
	}
	// 现在为节点2设置流处理器
	unreachable2.SetStreamHandler("/customprotocol", func(s network.Stream) {
		log.Println("Awesome! We're now communicating via the relay!")
		// End the example
		s.Close()
	})
	// 节点2向中继节点relay1预留一个槽位,以便后续中继节点转发需要发送到node2的请求
	// 但我们还没告诉relay1如何识别node2的请求
	_, err = client.Reserve(context.Background(), unreachable2, relay1info)
	// 为node2创建一个新的地址
	relayaddr, err := ma.NewMultiaddr("/p2p/" + relay1info.ID.String() + "/p2p-circuit/p2p/" + unreachable2.ID().String())
	//由于我们刚刚尝试拨号失败,拨号系统将默认阻止我们如此迅速地重新拨号。既然我们知道我们在做什么,我们可以使用这个丑陋的hack(它在我们的TODO列表中,使它更简洁)来告诉拨号器“不,没关系,让我们再试一次”。
	// 清理上一次的拨号失败记录,因为拨号系统会
	unreachable1.Network().(*swarm.Swarm).Backoff().Clear(unreachable2.ID())
	// 通过中继地址打开到先前不可达主机的连接(relayaddr这是节点2的新地址,这是经过中继relay1的地址)
	unreachable2relayinfo := peer.AddrInfo{
		ID:    unreachable2.ID(),
		Addrs: []ma.Multiaddr{relayaddr},
	}
	// 拨号成功后会将其加入到自己的地址库
	if err := unreachable1.Connect(context.Background(), unreachable2relayinfo); err != nil {
		log.Printf("Unexpected error here. Failed to connect unreachable1 and unreachable2: %v", err)
		return
	}
	log.Println("Yep, that worked!")
	//因为我们没有到目标节点的直接连接——我们有一个中继连接——连接被标记为瞬态。由于中继限制了可以通过中继连接交换的数据量,因此应用程序需要显式地选择使用中继连接。一般来说,只有在带宽要求较低的情况下,我们才应该这样做,并且当中继连接被直接(holepched)连接取代时,我们很高兴连接被终止。
	// 节点1尝试连接到节点2的customprotocol的协议上
	s, err := unreachable1.NewStream(network.WithUseTransient(context.Background(), "customprotocol"), unreachable2.ID(), "/customprotocol")
	s.Read(make([]byte, 1))
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值