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 testnameCompiling libp2p_demo v0.1.0 (C:\Users\kingchuxing\Documents\learning-libp2p-main\rust)Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.47sRunning `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 testnameFinished `dev` profile [unoptimized + debuginfo] target(s) in 0.32sRunning `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:providegethelp 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)