前言:
眼前兄弟们对“p2p应用程序”可能比较关注,大家都想要分析一些“p2p应用程序”的相关资讯。那么小编也在网上收集了一些对于“p2p应用程序””的相关知识,希望大家能喜欢,朋友们一起来学习一下吧!本章涵盖对等网络简介了解libp2p网络的核心架构在对等节点之间交换 ping 命令在 p2p 网络中发现对等方
在上一章中,我们介绍了异步编程的基础知识,以及如何使用 Rust 编写异步代码。在本章中,我们将构建一些使用低级 P2P 网络库和使用 Rust 异步编程的 p2p 应用程序的简单示例。
但是为什么要了解P2P呢?
P2P是一种网络技术,可以在不同计算机之间共享各种计算资源,例如CPU,网络带宽和存储。P2P是当今一种非常常用的方法,用于在用户之间在线共享文件(例如音乐,图像和其他数字媒体)。Bittorrent和Gnutella是流行的文件共享p2p应用程序的例子。它们不依赖中央服务器或中介来连接多个客户端。最重要的是,它们利用用户的计算机作为客户端和服务器,从而将计算从中央服务器中卸载出来。P2P网络如何运作,它们有何不同?
让我们深入研究点对点网络背后的基本概念。
11.1 点对点网络简介
部署在企业或 Web 内的传统分布式系统使用客户端-服务器范例。Web浏览器和Web服务器一起作为客户端-服务器系统的一个很好的例子,其中Web浏览器(客户端)在Web服务器(服务器)上托管的特定资源上请求信息(例如,GET请求)或计算(例如,POST/PUT/DELETE 请求)。然后,Web 服务器确定客户端是否有权接收该信息或执行该计算,然后完成请求。
点对点网络(P2P)是另一种类型的分布式系统。在P2P中,一组节点(或对等节点)直接相互交互以共同提供公共服务,而无需中央协调器或管理员。点对点系统的例子包括文件共享网络,如IPFS和Bittorrent,以及区块链网络,如比特币和以太坊。P2P系统中的每个节点(或对等节点)都可以充当客户端(从其他节点请求信息)和服务器(存储/检索数据并执行必要的计算以响应客户端请求)。虽然 P2P 网络中的所有节点不必相同,但区分客户端-服务器网络与 P2P 网络的一个关键特征是没有具有独特权限的专用服务器。在开放、无需许可的 P2P 网络中,任何节点都可以决定提供与 P2P 节点关联的全部或部分服务集。
与客户端-服务器网络相比,P2P网络可以在其上构建不同类型的应用程序,这些应用程序无需许可,容错和抗审查。
无需许可,因为没有服务器可以切断对客户端信息的访问,因为数据和状态跨多个节点复制。
容错,因为没有单点故障,例如中央服务器。
像区块链等网络一样抗审查。
P2P计算还可以更好地利用资源。想象一下,网络边缘的客户端可用的所有网络带宽、存储和处理能力,这些带宽、存储和处理能力在客户端-服务器计算中未被利用。
图 11.1 说明了客户端-服务器和 p2p 网络之间的差异。请注意,我们将在 p2p 网络的上下文中互换使用术语节点和对等节点。
然而,构建P2P系统可能比传统的客户端-服务器系统更复杂。与构建P2P系统相关的一些技术要求包括:
传输:P2P网络中的每个对等体都可以使用不同的协议,例如HTTP,TCP,UDP等。标识:每个对等方都需要知道它要连接并发送消息的对等方的身份。安全性:每个对等方都应该能够以安全的方式与其他对等方通信,而不会有第三方拦截或修改消息的风险对等路由:每个对等方可以通过各种路由(例如数据包在 IP 协议中的分布方式)接收来自其他对等方的消息,这意味着如果消息不是针对自己的,则每个对等方都应该能够将消息路由到其他对等方。消息传递:P2P网络应该能够发送点对点消息或组消息(以发布/订阅模式)
让我们仔细看看这些要求中的每一个:
传输:TCP/IP 和 UDP 协议无处不在,在编写网络应用程序时很受欢迎。但是还有其他更高级别的协议,如HTTP(通过TCP分层)和QUIC(通过UDP分层)。P2P网络中的每个对等体都应该能够发起与另一个节点的连接,并且由于网络中对等节点的多样性,能够侦听通过多个协议的传入连接。
对等身份:与 Web 开发域不同,在 Web 开发域中,服务器由唯一域名(例如 ,然后使用域名服务解析为服务器的 IP 地址),对等网络中的节点需要唯一标识,以便其他节点可以访问它们。对等网络中的节点使用公钥和私钥对(非对称公钥加密)与其他节点建立安全通信。对等网络中节点的身份称为 PeerId,它是节点公钥的加密哈希。
安全性:加密密钥对和 PeerId 使节点能够与其对等节点建立安全、经过身份验证的通信通道。但这只是安全性的一个方面。节点还需要实现授权框架,这些框架为哪个节点可以执行哪些类型的操作建立了规则。还有一些网络级安全威胁需要解决,例如女巫攻击(其中一个节点运营商启动大量具有不同身份的节点以获得网络中的优势地位)或 eclipse 攻击(一组恶意节点串通针对特定节点,使后者无法访问任何合法节点)。有关Sybil和eclipse攻击之间差异的更多信息可以在互联网上公开找到。
对等路由:P2P网络中的节点首先需要找到其他对等节点才能进行通信。这是通过维护对等路由表来实现的,该表包含对网络中其他对等方的引用。但是在有数千个或更多节点动态变化(即节点加入和离开网络)的P2P网络中,任何单个节点都很难为网络中的所有节点维护完整而准确的路由表。对等路由使节点能够将不适合它们的消息路由到目标节点。
消息传递:P2P网络中的节点可以向特定节点发送消息,但也可以参与广播消息传递协议。一个例子是发布/订阅,其中节点注册对特定主题的兴趣(订阅),并且订阅该主题的所有节点都会收到有关该主题的消息(发布)。此技术通常用于将消息的内容传输到整个网络。请注意,发布/订阅是分布式系统中发送方和接收方之间的消息传递的众所周知的体系结构模式。
流多路复用:我们之前已经看到(在前面关于传输的段落中)P2P网络中的节点如何支持多个传输。流多路复用是一种通过公共通信链路发送多个信息流的方法。在P2P的情况下,它允许多个独立的“逻辑”流共享一个共同的P2P传输层。当考虑节点与不同对等方具有多个通信流的可能性,或者两个远程节点之间也可能有许多并发连接的可能性时,这一点变得很重要。流多路复用有助于优化在对等方之间建立连接的开销。(注意:多路复用在后端服务开发中很常见,其中客户端可以与服务器建立底层网络连接,然后通过底层网络连接多路复用不同的流(每个流都有唯一的端口号)。
在本节中,我们研究了点对点系统设计中涉及的一些基本概念。在下一节中,我们将仔细研究一个用于 P2P 网络的流行 Rust 库,因为我们将在后面的部分中使用此库编写一些异步 Rust 代码。
11.2 了解libp2p网络的核心架构
为 P2P 应用程序编写自己的网络层是一项艰巨的任务。此外,如果有人已经完成了艰苦的工作,我们不想重新发明轮子。因此,我们将使用一个名为libp2p的低级p2p网络库,这使得构建P2P应用程序变得更加容易。
更具体地说,libp2p 是一个协议、规范和库的模块化系统,支持点对点应用程序的开发。libp2p在撰写本文时支持三种编程语言 - Go,Javascript和Rust。libp2p被许多流行的项目使用,如IPFS,Filecoin和Polkadot。
图 11.2 突出显示了用于构建强大的点对点网络的 libp2p 关键模块。
传输:负责从一个对等节点到另一个对等节点的实际传输和接收数据
身份:libp2p使用公钥加密(PKI)作为对等节点身份的基础。使用加密算法为每个节点生成唯一的对等 ID。
安全性:节点使用其私钥对消息进行签名。此外,节点之间的传输连接可以升级为安全的加密通道,以便远程对等方可以相互信任,并且没有第三方可以拦截它们之间的通信。
对等发现:使对等方能够在libp2p网络中相互查找和通信
对等路由:利用其他对等节点的知识启用与对等节点的通信。
内容路由:使对等节点能够从其他对等节点获取一段内容,而无需知道哪个对等节点拥有该内容
PubSub:允许向对某个主题感兴趣的一组对等方发送消息。
在本章中,您将学习如何利用 libp2p 协议的一个子集来使用 Rust 构建 p2p 应用程序。
现在让我们从 Rust libp2p 库的几个核心原语开始,使用代码示例。
11.2.1 对等 ID 和密钥对
让我们从为 P2P 节点生成对等 ID 和密钥对开始。
p2p 节点使用加密密钥对对消息进行签名,对等 ID 表示用于唯一标识 p2p 网络上节点的唯一对等身份。
开始一个新项目cargo new p2p-learn
在 Cargo.toml 中,添加以下条目:
libp2p = "0.42.2" #1tokio = { version = "1.16.1", features = ["full"] } #1
在 src 文件夹下创建一个文件夹箱。创建一个新文件 src/bin/iter1.rs,并向其中添加以下代码。
use libp2p::{identity, PeerId}; #1#[tokio::main] #2async fn main() { #3 let new_key = identity::Keypair::generate_ed25519(); #4 let new_peer_id = PeerId::from(new_key.public()); #5 println!("New peer id: {:?}", new_peer_id);}什么是公钥和私钥?
加密身份使用公钥基础结构 (PKI),该基础结构广泛用于为用户、设备和应用程序提供唯一身份,并保护端到端通信。它的工作原理是创建两个不同的加密密钥,也称为由私钥和公钥组成的密钥对,它们之间具有数学关系。密钥对有许多广泛的应用,但在 P2P 网络中,节点使用密钥对相互识别和验证自己。公钥可以与网络中的其他人共享,但节点的私钥绝不能泄露。
使用密钥对的一个很好的例子是在传统的服务器访问中。例如,如果要连接到数据中心或云中托管的远程服务器(使用 ssh),则可以配置密钥对进行访问,而不是使用密码。在此示例中,用户可以生成密钥对并在远程服务器上配置公钥,从而向用户授予访问权限。但是远程服务器如何知道哪个用户是该公钥的所有者?要启用此功能,在(通过 SSH )连接到远程服务器时,用户必须指定私钥(与存储在服务器上的公钥相关联)。私钥永远不会发送到远程服务器,但 SSH 客户端(在本地服务器上运行)使用用户的私钥向远程 SSH 服务器验证自身。
私钥和公钥还有许多其他用途,例如加密/解密和数字签名,但这超出了本章的范围。
使用 运行程序,您应该会看到类似以下内容打印到您的终端:cargo run --bin iter1
New peer id: PeerId("12D3KooWBu3fmjZgSMLkQ2p1DG35UmEayYBrhsk6WEe1xco1JFbV")
在libp2p中,对等方的身份在对等方的整个生命周期内是稳定且可验证的。但是,libp2p 区分了对等方的身份及其位置。如前所述,对等方的身份是对等方 ID。对等方的位置是可以访问对等方的网络地址。例如,可以通过TCP,websockets,QUIC或任何其他协议访问对等体。libp2p 以称为多地址 (multiaddr) 的自描述格式对这些网络地址进行编码。因此,在libp2p中,多地址表示对等体的位置。我们将在下一节中了解如何使用多地址。
11.2.2 多地址
当人们分享联系信息时,他们会使用他们的电话号码、社交媒体资料或实际位置地址(在收到货物的情况下)。当 p2p 网络上的节点共享其联系信息时,它们会发送一个包含网络地址和对等 ID 的多地址。
节点的多地址的对等 id 组件表示如下:
/p2p/12D3KooWBu3fmjZgSMLkQ2p1DG35UmEayYBrhsk6WEe1xco1JFbV
字符串 12D3KooWBu3fmjZgSMLkQ2p1DG35UmEayYBrhsk6WEe1xco1JFbV 表示节点的对等 ID。回想一下,我们在上一节中学习了如何为节点生成对等 ID。
多地址的网络地址组件(也称为传输地址)如下所示:
/ip4/192.158.1.23/tcp/1234
这表示IPv4是使用的传输协议,IP地址为192.158.1.23,它侦听的TCP端口为1234。
节点的完整多地址只是对等 ID 和网络地址的组合,如下所示:
/ip4/192.158.1.23/tcp/1234/p2p/12D3KooWBu3fmjZgSMLkQ2p1DG35UmEayYBrhsk6WEe1xco1JFbV
对等方以此处显示的格式与其他对等方交换此多地址。
libp2p 库使用 DNS 协议在内部将此“基于名称”的地址 - /ip4/192.158.1.23 转换为常规 IP 地址。
我们将在下一节中看到代码中多地址的用法。
11.2.3 群和网络行为
Swarm 是 libp2p 中给定 P2P 节点中的网络管理器模块。它维护从给定节点到远程节点的所有活动和挂起的连接,并管理已打开的所有子流的状态。
Swarm 的结构和上下文如图 11.5 所示,本节将进一步详细解释。
现在让我们扩展前面的示例。创建一个新文件 src/bin/iter2.rs 并添加以下代码:
use libp2p::swarm::{DummyBehaviour, Swarm, SwarmEvent}; #1use libp2p::futures::StreamExt; #2use libp2p::{identity, PeerId};use std::error::Error;#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let new_key = identity::Keypair::generate_ed25519(); let new_peer_id = PeerId::from(new_key.public()); println!("local peer id is: {:?}", new_peer_id); let behaviour = DummyBehaviour::default(); #3 let transport = libp2p::development_transport(new_key).await?; #4 let mut swarm = Swarm::new(transport, behaviour, new_peer_id); #5 swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; #6 loop { match swarm.select_next_some().await { #7 SwarmEvent::NewListenAddr { address, .. } => { #8 println!("Listening on local address {:?}", address) } _ => {} } }}
参考标注<1>,在能够与其他节点通信之前,需要先构造一个群网络管理器。Swarm 代表一个低级接口,并提供对 libp2p 网络的细粒度控制。Swarm 是使用节点的传输、网络行为和对等 ID 的组合构建的。我们之前已经看到了什么是运输和peer_id。现在让我们看看什么是网络行为。
虽然传输指定如何通过网络发送字节,但网络行为指定要发送的字节以及发送给谁。libp2p 中的网络行为示例包括 Ping(节点发送和响应 ping 消息)、用于发现网络上其他对等节点的 mDNS 和 Kademlia(用于对等路由和内容路由功能)。在我们的示例中,为了简单起见,我们使用了虚拟网络行为。多个网络行为可以与单个运行节点相关联。
接下来让我们看一下注释 <7> 中的代码行:swarm.select_next_some().await。await 关键字用于计划异步任务以轮询协议和连接,准备就绪后,将接收 swarm 事件。当没有要处理的内容时,任务将处于空闲状态,群将输出 Poll::P ending。这是异步 Rust 的另一个例子。
需要注意的一点是,相同的代码在libp2p网络的所有节点上运行,这与客户端-服务器模型不同,客户端和服务器具有不同的代码库。
让我们按照此处所述运行代码。
在计算机上创建两个终端会话。从第一个终端,从项目根目录,运行:
cargo run --bin iter2
您应该看到类似于下面的输出打印到第一个节点的终端。
local peer id is: PeerId("12D3KooWByvE1LD4W1oaD2AgeVWAEu9eK4RtD3GuKU1jVEZUvzNm") #1Listening on local address "/ip4/127.0.0.1/tcp/55436" #2Listening on local address "/ip4/192.168.1.74/tcp/55436"
从第二个终端,从项目根目录,运行以下命令:
cargo run --bin iter2
您应该看到第二个节点的终端输出类似于以下内容:
local peer id is: PeerId("12D3KooWQiQZA5zcLzhF86kuRoq9f6yAgiLtGqD5bDG516kVzW46")Listening on local address "/ip4/127.0.0.1/tcp/55501"Listening on local address "/ip4/192.168.1.74/tcp/55501"
同样,您可以看到 node2 正在侦听的本地地址(打印到终端)。
如果你走到了这一步,这是一个好的开始。
但是,此代码中没有任何有趣的事情发生。我们能够启动两个节点并要求它们相互连接。但是我们不知道连接是否正确建立,或者两者是否可以通信。让我们在下一节中增强此代码,以便在节点之间交换 ping 命令。
11.3 在对等节点之间交换 ping 命令
创建一个新文件 src/bin/iter3.rs 并添加以下代码:
use libp2p::swarm::{Swarm, SwarmEvent};use libp2p::futures::StreamExt;use libp2p::ping::{Ping, PingConfig};use libp2p::{identity, Multiaddr, PeerId};use std::error::Error;#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let new_key = identity::Keypair::generate_ed25519(); let new_peer_id = PeerId::from(new_key.public()); println!("local peer id is: {:?}", new_peer_id); let transport = libp2p::development_transport(new_key).await?; let behaviour = Ping::new(PingConfig::new().with_keep_alive(true)); #1 let mut swarm = Swarm::new(transport, behaviour, new_peer_id); swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; #2 if let Some(remote_peer) = std::env::args().nth(1) { #3 let remote_peer_multiaddr: Multiaddr = remote_peer.parse()?; swarm.dial(remote_peer_multiaddr)?; println!("Dialed remote peer: {:?}", remote_peer); #3 } loop { match swarm.select_next_some().await { #4 SwarmEvent::NewListenAddr { address, .. } => { println!("Listening on local address {:?}", address) } SwarmEvent::Behaviour(event) => println!("Event received from peer is {:?}", event), #5 _ => {} } }}
在注释 <2> 中,0.0.0.0 表示本地计算机上的所有 IPv4 地址。例如,如果主机有两个 IP 地址 192.168.1.2 和 10.0.0.1,并且主机上运行的服务器侦听 0.0.0.0,则可以在两个 IP 上访问该 IP 地址。0 端口表示选择一个随机可用端口。
在注释 <3> 中,从命令行参数解析远程节点多地址。然后,本地节点在此多地址上建立与远程节点的连接。
现在,让我们使用两个节点构建并测试此 p2p 示例。
在计算机上创建两个终端会话。从第一个终端,从项目根目录,运行:
cargo run --bin iter3
我们称之为节点 1。
您应该看到类似于下面的输出打印到第一个节点的终端。
local peer id is: PeerId("12D3KooWByvE1LD4W1oaD2AgeVWAEu9eK4RtD3GuKU1jVEZUvzNm")Listening on local address "/ip4/127.0.0.1/tcp/55872"Listening on local address "/ip4/192.168.1.74/tcp/55872"
请注意,此时没有要连接的远程节点,因此本地节点仅打印出侦听事件以及它正在侦听新连接的多地址。因此,Ping 网络行为(即使已在本地节点中配置)仍处于活动状态。为此,我们需要启动第二个节点。
从第二个终端,从项目根目录,运行以下命令。确保在命令行参数中指定第一个节点的多地址。
cargo run --bin iter3 /ip4/127.0.0.1/tcp/55872
我们称之为节点 2。
此时node2已经启动,它还将打印出它正在侦听的本地地址。由于已指定远程节点多地址,因此节点 2 与节点 1 建立连接,然后开始侦听事件。收到来自节点 2 的传入连接后,节点 1 将 ping 消息发送到节点 2,节点 2 以 pong 消息进行响应。这些消息应该开始出现在节点 1 和节点 2 的终端上,并在时间间隔后(大约每 15 秒左右)继续循环。另请注意,P2P 节点使用与 Tokio 运行时的异步 Rust 来执行并发任务,以处理来自远程节点的多个数据流和事件。
在本节中,我们已经看到了如何让两个 P2P 节点相互交换 ping 消息。在此示例中,我们通过指定节点 2 正在侦听的多地址将节点 1 连接到节点 1。但在P2P网络中,节点是动态加入和离开的。在下一节中,我们将看到 Peer 节点如何在 p2p 网络上发现彼此。
11.4 发现对等方
这次让我们编写一个P2P节点,以便在启动时自动检测网络上的其他节点。
use libp2p::{ futures::StreamExt, identity, mdns::{Mdns, MdnsConfig, MdnsEvent}, swarm::{Swarm, SwarmEvent}, PeerId,};use std::error::Error;#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let id_keys = identity::Keypair::generate_ed25519(); let peer_id = PeerId::from(id_keys.public()); #1 println!("Local peer id: {:?}", peer_id); let transport = libp2p::development_transport(id_keys).await?; #2 let behaviour = Mdns::new(MdnsConfig::default()).await?; #3 let mut swarm = Swarm::new(transport, behaviour, peer_id); #4 swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; loop { match swarm.select_next_some().await { SwarmEvent::NewListenAddr { address, .. } => { println!("Listening on local address {:?}", address) } SwarmEvent::Behaviour(MdnsEvent::Discovered(peers)) => { for (peer, addr) in peers { println!("discovered {} {}", peer, addr); } } SwarmEvent::Behaviour(MdnsEvent::Expired(expired)) => { for (peer, addr) in expired { println!("expired {} {}", peer, addr); } } _ => {} } }}
mDNS 是由 RFC 6762 () 定义的协议,它将主机名解析为 IP 地址。在libp2p中,它用于发现网络上的其他节点。
在libp2p中实现的网络行为mDNS将自动发现本地网络上的其他libp2p节点。
让我们通过构建和运行代码来了解这一点:
cargo run --bin iter4
我们称之为节点 1。
你会看到类似的东西打印到 node1 的终端窗口中:
Local peer id: PeerId("12D3KooWNgYbVg8ZyJ4ict2N1hdJLKoydB5sTqwiWN2SHtC3HwWt")Listening on local address "/ip4/127.0.0.1/tcp/50960"Listening on local address "/ip4/192.168.1.74/tcp/50960"
请注意,在此示例中,节点 1 正在侦听 TCP 端口 50960。
然后从终端 2,使用相同的命令运行程序。请注意,与以前不同的是,我们没有指定节点 1 的多地址。
cargo run --bin iter4
我们称之为节点 2。
您应该能够看到打印到 node2 终端的类似消息。
Local peer id: PeerId("12D3KooWCVVb2EyxB1WdAcLeMuyaJ7nnfUCq45YNNuFYcZPGBY1f")Listening on local address "/ip4/127.0.0.1/tcp/50967"Listening on local address "/ip4/192.168.1.74/tcp/50967"discovered 12D3KooWNgYbVg8ZyJ4ict2N1hdJLKoydB5sTqwiWN2SHtC3HwWt /ip4/192.168.1.74/tcp/50960discovered 12D3KooWNgYbVg8ZyJ4ict2N1hdJLKoydB5sTqwiWN2SHtC3HwWt /ip4/127.0.0.1/tcp/50960
请注意,node2 能够发现节点 1 侦听端口 50960,这是节点 1 正在侦听的端口。而 node2 本身正在侦听端口 50967 上的新事件和消息。
从另一个终端启动第三个节点 (node3)。您应该看到以下内容:
cargo run --bin iter4
您将在 node3 的终端上看到以下消息:
Local peer id: PeerId("12D3KooWC95ziPjTXvKPNgoz3CSe2yp6SBtKh785eTdY5L2YK7Tc")Listening on local address "/ip4/127.0.0.1/tcp/50996"Listening on local address "/ip4/192.168.1.74/tcp/50996"discovered 12D3KooWCVVb2EyxB1WdAcLeMuyaJ7nnfUCq45YNNuFYcZPGBY1f /ip4/192.168.1.74/tcp/50967discovered 12D3KooWCVVb2EyxB1WdAcLeMuyaJ7nnfUCq45YNNuFYcZPGBY1f /ip4/127.0.0.1/tcp/50967discovered 12D3KooWNgYbVg8ZyJ4ict2N1hdJLKoydB5sTqwiWN2SHtC3HwWt /ip4/192.168.1.74/tcp/50960discovered 12D3KooWNgYbVg8ZyJ4ict2N1hdJLKoydB5sTqwiWN2SHtC3HwWt /ip4/127.0.0.1/tcp/50960
请注意,node3 已发现在端口 1 上侦听的节点 50960 和在端口 2 上侦听的节点 50967。
这看起来微不足道,直到您意识到我们没有告诉 node3 其他两个节点在哪里运行。使用 mDNS 协议,node3 能够检测并连接到本地网络上的其他 libp2p 节点。
11.5 小结在客户端-服务器计算模型中,客户端和服务器表示两个不同的软件,其中服务器是数据和相关计算的保管人,客户端请求服务器发送数据或对服务器管理的资源执行计算。在P2P网络中,通信发生在对等节点之间,每个节点都可以执行客户端和服务器的角色。区分客户端-服务器网络与P2P网络的一个关键特征是没有具有独特权限的专用服务器libp2p 是一个由协议、规范和库组成的模块化系统,支持开发点对点应用程序。它被用于许多著名的p2p项目。libp2p 的关键架构组件包括传输、身份、安全性、对等发现、对等路由、内容路由和消息传递。使用代码示例,我们研究了如何为节点生成唯一的对等 ID,其他节点可以使用该 ID 来唯一标识它。我们还深入研究了多地址的基础知识,以及它们如何表示通过P2P网络与节点通信的完整路径。节点的对等 ID 是节点整体多地址的一部分。我们编写了一个 Rust 程序,其中节点在它们之间交换简单的乒乓命令。此示例演示了如何为节点配置 swarm 网络管理对象,以侦听 p2p 网络上的特定事件并执行操作。在本章的最后,我们编写了另一个带有libp2p库的Rust程序,该程序展示了对等节点如何使用mDNS协议在p2p网络上发现彼此。
对于寻求其他代码挑战的读者,这里有几个可以使用libp2p构建的P2P应用程序:
实现一个简单的 p2p 聊天应用程序实现分布式 p2p 键值存储。实现分布式文件存储网络(如IPFS)
提示:libp2p 库有几个预构建的代码示例,可以参考这些示例来实现这些练习。本章末尾提供了对代码存储库的引用。
至此,我们结束了本章,以及有关高级主题的本节。在下一章(也是最后一章)中,我们将学习如何为 Rust 服务器和应用程序准备生产部署。
下一章见!
标签: #p2p应用程序