p2p、分布式,区块链笔记:libp2p通过libp2p_demo::network实现文件传递功能

代码

  • 代码来自github开源项目file-sharing.rs。主要依赖clap库进行命令行参数解析,使用async_std进行并行操作,使用libp2p_demo::network中的相关方法进行网络建立与文件传输,但是代码量却减少了很多,这是由于libp2p_demo::network对libp2p中的集群事件等相关方法,将文件传递功能进行了集成。
use async_std::task::spawn;
use clap::Parser;
use futures::prelude::*;
use libp2p::core::{Multiaddr, PeerId};
use libp2p::multiaddr::Protocol;
use libp2p_demo::network;
use std::error::Error;
use std::io::Write;
use std::path::PathBuf;

#[async_std::main]
async fn main() -> Result<(), Box<dyn Error>> {
    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方法连接到该节点。
    if let Some(addr) = opt.peer {
        let peer_id = match addr.iter().last() {
            Some(Protocol::P2p(hash)) => PeerId::from_multihash(hash).expect("Valid hash."),
            _ => return Err("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() {
                return Err(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| {
                let mut network_client = network_client.clone();
                let name = name.clone();
                async move { 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"。
struct Opt {
    ///  #[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)]
enum CliArgument {
    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"

接收端

  • 由于最后需要用std::io::stdout().write_all(&file_content)?;输出文件内容,所以采用了txt格式的文件进行测试
  • 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)

相关推荐

  1. libp2p-echo案例】

    2024-07-10 13:10:01       43 阅读
  2. [libp2p的一些核心概念]

    2024-07-10 13:10:01       44 阅读
  3. libp2p服务发现之 Multicast DNS(mDNS)

    2024-07-10 13:10:01       33 阅读
  4. <span style='color:red;'>P</span><span style='color:red;'>2</span><span style='color:red;'>P</span>应用

    P2P应用

    2024-07-10 13:10:01      51 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 13:10:01       4 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 13:10:01       5 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 13:10:01       4 阅读
  4. Python语言-面向对象

    2024-07-10 13:10:01       5 阅读

热门阅读

  1. 精通C#编程需要学习哪些常用框架?

    2024-07-10 13:10:01       8 阅读
  2. Redis高可用解决方案哨兵模式与集群模式的比较

    2024-07-10 13:10:01       7 阅读
  3. C#实用的工具类库

    2024-07-10 13:10:01       10 阅读
  4. 4085行代码还原2D我的世界(上)

    2024-07-10 13:10:01       8 阅读
  5. 大数据面试题之GreenPlum(1)

    2024-07-10 13:10:01       10 阅读
  6. 量化机器人能否识别市场机会?

    2024-07-10 13:10:01       9 阅读
  7. 探讨SpringMVC的工作原理

    2024-07-10 13:10:01       9 阅读
  8. CSS布局艺术:掌握水平与垂直对齐的秘诀

    2024-07-10 13:10:01       6 阅读
  9. SQL 游标

    2024-07-10 13:10:01       8 阅读
  10. 0706_ARM8

    2024-07-10 13:10:01       11 阅读