本系列文章首先研究 P2P 网络的核心概念,然后详细分析 libp2p-rust 库中的应用实例,为以后开发 P2P 网络应用程序打好基础。
P2P 网络
P2P(Peer-to-Peer)是一种网络技术,可以在网络中不同的计算机上共享各种计算资源,如 CPU、网络带宽和存储等。P2P 技术应用最广泛的是在网络中共享文件以及区块链网络,它们不依赖中央服务器或中介来连接多个客户端,用户的计算机即是客户端也是服务器。
由于 P2P 网络是一种分布式系统,不会像中央服务器一样存在单点故障,因此容错性极强。
下面让我们来看一看 P2P 网络的核心概念:
传输协议
在 P2P 网络底层一般使用 TCP/UDP 传输层协议。由于 P2P 节点应用的多样性,在 TCP/UDP 传输层协议之上,会使用多种应用层协议,如:HTTP,gRPC 及自定义协议等。为了有效利用资源,P2P 网络会在一个连接上监听、解析多种协议,即多路复用技术:多个逻辑子流可以在同一个底层(TCP)连接上共存。可以查看 yamux 库了解更多细节。
节点标识
P2P 网络中的节点需要一个唯一的标识,以便其他节点能够找到它们。P2P 网络中的节点使用公钥和私钥对(非对称公钥加密)与其他节点建立安全通信。在 P2P 网络中节点标识被称为 PeerId,它是通过对节点公钥进行加密哈希得到的。
安全规则
密钥对和节点身份标识使节点之间能够体建立安全的、经过身份验证的通信通道。但这只是安全的一个方面,节点还需要根据业务逻辑实现授权框架,该框架建立一些规则:哪些节点可以执行哪种类型的操作等。
节点路由
P2P 网络中的一个节点首先需要找到其他节点来进行通信。这是通过维护一个节点路由表来实现的。但是在 P2P 网络中,有成千上万个节点在动态变化(即节点的加入和离开),单个节点很难为网络中的所有节点维护一个完整、准确的路由表。所以节点路由表通常会由一系列路由节点维护。
消息
P2P 网络中的节点可以向特定节点发送消息,也可以广播消息。使用发布/订阅模式,节点订阅感兴趣 Topic,所有订阅该 Topic 的节点都能接收和发送消息。这种技术也通常用于将消息的内容传输到整个网络。
流多路复用
在 P2P 网络中,允许多个独立的“逻辑”流共享一个公共的 P2P 传输层。流多路复用有助于优化节点之间建立网络连接的开销。多路复用在后端服务开发中很常见,客户端可以与服务器建立底层网络连接,然后在底层网络连接上复用不同的流。
libp2p
自己编写 P2P 应用程序的网络层是一项庞大的工程,我们将使用底层 p2p 网络库—libp2p,在其上构建 p2p 应用程序会更加容易。libp2p 是一个模块化的系统,支持三种编程语言:Rust、Go、JS。许多流行的项目中都使用 libp2p 做为 P2P 网络底层,如 IPFS、 Filecoin、Polkadot 和 Substrate。
libp2p 将 P2P 网络基本概念分解成了不同的模块,可以在不同的应用场景中组合使用。
我们先通过 Ping 这个简单的程序来熟悉一下 libp2p 的组件及如何使用 libp2p 开发点对点网络。
PING
这个例子非常简单,主要就是一个节点向另一个节点发送 ping 消息,然后等待另一个节点返回 pong 消息。
新建一个项目名叫:libp2p-learn
master:p2p Justin$ cargo new libp2p-learn
Created binary (application) `libp2p-learn` package
master:p2p Justin$ cd libp2p-learn/
master:libp2p-learn Justin$ code .
复制代码
在 Cargo.toml 文件中加入 libp2p 和 tokio 依赖:
[dependencies]
libp2p = "0.46"
tokio = { version = "1.19", features = ["full"] }
复制代码
然后在 src/bin/目录下创建 ping.rs 文件:
use std::error::Error;
use libp2p::{
futures::StreamExt,
identity,
ping::{Ping, PingConfig},
swarm::SwarmEvent,
Multiaddr, PeerId, Swarm,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// 生成密钥对
let key_pair = identity::Keypair::generate_ed25519();
// 基于密钥对的公钥,生成节点唯一标识peerId
let peer_id = PeerId::from(key_pair.public());
println!("节点ID: {peer_id}");
// 声明Ping网络行为
let behaviour = Ping::new(PingConfig::new().with_keep_alive(true));
// 传输
let transport = libp2p::development_transport(key_pair).await?;
// 网络管理模块
let mut swarm = Swarm::new(transport, behaviour, peer_id);
// 在节点随机开启一个端口监听
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
// 从命令行参数获取远程节点地址,进行链接。
if let Some(remote_peer) = std::env::args().nth(1) {
let remote_peer_multiaddr: Multiaddr = remote_peer.parse()?;
swarm.dial(remote_peer_multiaddr)?;
println!("链接远程节点: {remote_peer}");
}
loop {
// 匹配网络事件
match swarm.select_next_some().await {
// 监听事件
SwarmEvent::NewListenAddr { address, .. } => {
println!("本地监听地址: {address}");
}
// 网络行为事件
SwarmEvent::Behaviour(event) => println!("{:?}", event),
_ => {}
}
}
}
复制代码
网络行为 Behaviour:传输(transport)定义如何在网络中发送字节流,而网络行为定义发送什么样的字节流,在这里我们发送 ping/pong 消息。
网络管理模块 Swarm:用于管理节点之间的所有活跃连接和挂起连接,并管理所有已打开的子流状态。Swarm 是通过传输、网络行为和节点 PeerId 来创建。
节点地址:/ip4/0.0.0.0/tcp/0,表示在本机所有 ip 地址上,开一个随机的 Tcp 端口进行监听。
打开一个终端,运行:
master:libp2p-learn Justin$ cargo run --bin ping
Compiling libp2p-learn v0.1.0 (/Users/Justin/workspace_rust_exercise/network-study/p2p/libp2p-learn)
Finished dev [unoptimized + debuginfo] target(s) in 8.65s
Running `target/debug/ping`
节点ID: 12D3KooWR7H9SwB2yiFBKvzcVGFdpeKmuFG9qDTBTvuuuDarASST
本地监听地址: /ip4/127.0.0.1/tcp/58645
复制代码
可以看到已经打印出 PeerId 和监听地址。
打开另一个终端,运行:
cargo run --bin ping /ip4/127.0.0.1/tcp/58645
复制代码
master:libp2p-learn Justin$ cargo run --bin ping /ip4/127.0.0.1/tcp/58645
Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/ping /ip4/127.0.0.1/tcp/58645`
节点ID: 12D3KooWCUFTHNMJrR1p8vkFEFFYm4J8iPA1Wh6x2Dya5qmU1xdL
链接远程节点: /ip4/127.0.0.1/tcp/58645
本地监听地址: /ip4/127.0.0.1/tcp/58727
Event { peer: PeerId("12D3KooWR7H9SwB2yiFBKvzcVGFdpeKmuFG9qDTBTvuuuDarASST"), result: Ok(Pong) }
Event { peer: PeerId("12D3KooWR7H9SwB2yiFBKvzcVGFdpeKmuFG9qDTBTvuuuDarASST"), result: Ok(Ping { rtt: 1.234008ms }) }
复制代码
我们可以看到链接到刚刚的节点/ip4/127.0.0.1/tcp/58645 成功,也收到了发送过来的 ping/pong 的消息。
下一篇文章我们将详细解析 P2P 聊天程序。
评论