useasync_std::task::spawn;useclap::Parser;usefutures::prelude::*;uselibp2p::core::{Multiaddr,PeerId};uselibp2p::multiaddr::Protocol;uselibp2p_demo::network;usestd::error::Error;usestd::io::Write;usestd::path::PathBuf;#[async_std::main]asyncfnmain()->Result<(),Box<dynError>>{env_logger::init();let opt =Opt::parse();// network::new 返回值是一个元组,包含三个部分:// network_client(网络客户端:用于在应用程序的任何位置与网络层进行交互。)、// network_events(网络事件流 :用于接收传入请求的事件流)// network_event_loop(网络任务驱动:用于驱动网络本身的任务)// network::new来自libp2p_demo::network模块,从源代码看其为Swarm::new的进一步封装let(mut network_client,mut network_events, network_event_loop)=network::new(opt.secret_key_seed).await?;// async-std 是一个用于异步编程的库,类似于 tokio// async-std::task::spawn()函数会在后台启动一个新的异步任务,允许多个任务同时执行,这样可以提高程序的并发性能。// Spawn the network task for it to run in the background.spawn(network_event_loop.run());// 启动网络监听 In case a listen address was provided use it, otherwise listen on any address.match opt.listen_address {Some(addr)=> network_client
.start_listening(addr)// 侦听给定地址上的输入连接请求.await.expect("Listening not to fail."),None=> network_client
.start_listening("/ip4/0.0.0.0/tcp/0".parse()?).await.expect("Listening not to fail."),};// 连接到指定的对等节点,如果命令行参数指定了对等节点的地址(peer),则根据地址中的信息构建对等节点的ID,并尝试通过dial方法连接到该节点。ifletSome(addr)= opt.peer {let peer_id =match addr.iter().last(){Some(Protocol::P2p(hash))=>PeerId::from_multihash(hash).expect("Valid hash."),
_ =>returnErr("Expect peer multiaddr to contain peer ID.".into()),};
network_client
.dial(peer_id, addr).await.expect("Dial to succeed");}// 处理命令行参数中的不同操作match opt.argument {// 如果提供了一个文件,即{ path, name }。CliArgument::Provide{ path, name }=>{// Advertise oneself as a provider of the file on the DHT.
network_client.start_providing(name.clone()).await;// network_client.start_providing将本地节点作为DHT上给定文件的提供者进行播发。loop{match network_events.next().await{// Reply with the content of the file on incoming requests.Some(network::Event::InboundRequest{ request, channel })=>{if request == name {
network_client
.respond_file(std::fs::read(&path)?, channel).await;}}
e =>todo!("{:?}", e),}}}// 如果给出了一个名称,即{name}CliArgument::Get{ name }=>{// Locate all nodes providing the file.let providers = network_client.get_providers(name.clone()).await;if providers.is_empty(){returnErr(format!("Could not find provider for file {}.", name).into());}// Request the content of the file from each node.let requests = providers.into_iter().map(|p|{letmut network_client = network_client.clone();let name = name.clone();asyncmove{ network_client.request_file(p, name).await}.boxed()});// Await the requests, ignore the remaining once a single one succeeds.let file_content =futures::future::select_ok(requests).await.map_err(|_|"None of the providers returned file.")?.0;std::io::stdout().write_all(&file_content)?;}}Ok(())}#[derive(Parser, Debug)]// #[derive(Parser, Debug)]: 这是一个Rust的属性宏(attribute macro),用于自动为结构体实现解析器(parser)和调试输出(Debug trait)。Parser是由Clap库提供的,用于解析命令行参数#[clap(name = "libp2p file sharing example")]// #[clap(name = "libp2p file sharing example")]: 这个属性指定了生成的命令行接口的名称为 "libp2p file sharing example"。structOpt{/// #[clap(long)] // #[clap(long)]: 这个属性指示Clap库将下面的字段作为长格式命令行参数处理。长格式参数通常由两个破折号(--)引导,例如 --secret-key-seed。/// Fixed value to generate deterministic peer ID.#[clap(long)]
secret_key_seed:Option<u8>,#[clap(long)]
peer:Option<Multiaddr>,#[clap(long)]
listen_address:Option<Multiaddr>,#[clap(subcommand)]// #[clap(subcommand)] 属性,表示它是一个子命令(subcommand)。CliArgument 可能是一个枚举类型,用于定义不同的子命令选项。
argument:CliArgument,}// 有两个成员的枚举变量#[derive(Debug, Parser)]enumCliArgument{Provide{#[clap(long)]
path:PathBuf,#[clap(long)]
name:String,},Get{#[clap(long)]
name:String,},}
运行结果
发送端
cargo run --example 05-file-sharing -- --listen-address /ip4/127.0.0.1/tcp/40837 --secret-key-seed 1 provide --path ./test.exe --name testname
PS C:\Users\kingchuxing\Documents\learning-libp2p-main\rust> cargo run --example 05-file-sharing -- --listen-address /ip4/127.0.0.1/tcp/40837 --secret-key-seed 1 proet-key-seed 1 provide --path .\test.txt --name testname
Compiling libp2p_demo v0.1.0 (C:\Users\kingchuxing\Documents\learning-libp2p-main\rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.47s
Running `target\debug\examples\05-file-sharing.exe --listen-address /ip4/127.0.0.1/tcp/40837 --secret-key-seed 1 provide --path .\test.txt --name testname`
Local node is listening on "/ip4/127.0.0.1/tcp/40837/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X"
cargo run --example 05-file-sharing -- --peer /ip4/127.0.0.1/tcp/40837/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X get --name testname
PS C:\Users\kingchuxing\Documents\learning-libp2p-main\rust> cargo run --example 05-file-sharing -- --peer /ip4/127.0.0.1/tcp/40837/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X get --name testname
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
Running `target\debug\examples\05-file-sharing.exe --peer /ip4/127.0.0.1/tcp/40837/p2p/12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X get --name testname`
Local node is listening on "/ip4/192.168.3.12/tcp/62691/p2p/12D3KooWDDffKUnjVzsbMK5JHNQHsTE8FJNQDzS9tzKwSckGPe3f"
Local node is listening on "/ip4/127.0.0.1/tcp/62691/p2p/12D3KooWDDffKUnjVzsbMK5JHNQHsTE8FJNQDzS9tzKwSckGPe3f"
12345656867867867867867845634534523423453534564654645634 // test.txt文件的内容
参数解释
Usage: 05-file-sharing.exe [OPTIONS] <COMMAND>
Commands:
provide
get
help Print this message or the help of the given subcommand(s)
Options:
--secret-key-seed <SECRET_KEY_SEED> Fixed value to generate deterministic peer ID //用于决定生成peer ID的固定值
--peer <PEER>
--listen-address <LISTEN_ADDRESS>
-h, --help Print help
error: process didn't exit successfully: `target\debug\examples\05-file-sharing.exe` (exit code: 2)