导语:今天GitHub Trending上,一个Rust写的P2P网络库 iroh 突然爆火(326 stars/天)。它的口号很酷:"IP地址会失效,直接拨号密钥"。这篇教程手把手教你用它搭建一个去中心化的文件传输应用,零服务器成本。
一、前置条件
- Rust 1.75+(检查)
- 基础网络知识(了解TCP/UDP即可)
- 一台能联网的电脑(Windows/macOS/Linux均可)
安装Rust(如未安装):- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- source $HOME/.cargo/env
复制代码
二、iroh是什么?
iroh是一个模块化P2P网络栈,用Rust编写。核心特性:
- 密钥即地址:用Ed25519公钥作为节点标识,NAT穿透自动完成
- 模块化设计:只用你需要的部分(传输、发现、DHT等)
- 跨平台:支持桌面、移动端、甚至浏览器(WASM)
- 生产级:n0-computer公司维护,已用于实际产品
GitHub地址:https://github.com/n0-computer/iroh
三、实战:搭建P2P文件传输工具
步骤1:创建项目
- cargo new p2p-file-share
- cd p2p-file-share
复制代码
编辑 Cargo.toml 添加依赖:- [dependencies]
- iroh = "0.29"
- tokio = { version = "1", features = ["full"] }
- anyhow = "1"
复制代码
步骤2:编写发送端代码
创建 src/sender.rs:- use iroh::node::Node;
- use std::path::PathBuf;
- #[tokio::main]
- async fn main() -> anyhow::Result {
- // 启动本地节点
- let node = Node::memory().spawn().await?;
-
- // 打印节点地址(把这个发给接收方)
- let node_addr = node.node_addr().await?;
- println!("节点ID: {}", node_addr.node_id);
- println!("把上面这串ID发给接收方...");
-
- // 读取文件并发送
- let file_path = PathBuf::from("test.txt");
- let content = tokio::fs::read(&file_path).await?;
-
- // 通过blobs协议传输
- let hash = node.blobs().add_bytes(content).await?;
- println!("文件已上传,Hash: {}", hash);
-
- // 保持运行,等待接收方下载
- println!("按Ctrl+C退出...");
- tokio::signal::ctrl_c().await?;
- Ok(())
- }
复制代码
步骤3:编写接收端代码
创建 src/receiver.rs:- use iroh::node::Node;
- #[tokio::main]
- async fn main() -> anyhow::Result {
- let node = Node::memory().spawn().await?;
-
- // 从命令行读取发送方节点ID
- let args: Vec = std::env::args().collect();
- if args.len() ");
- return Ok(());
- }
-
- let sender_id = args[1].parse()?;
- let hash = args[2].parse()?;
-
- // 连接发送方并下载文件
- let content = node.blobs()
- .download(hash, sender_id)
- .await?;
-
- // 保存到本地
- tokio::fs::write("received.txt", content).await?;
- println!("文件下载完成!");
-
- Ok(())
- }
复制代码
步骤4:修改入口配置
编辑 Cargo.toml 添加二进制目标:- [[bin]]
- name = "sender"
- path = "src/sender.rs"
- [[bin]]
- name = "receiver"
- path = "src/receiver.rs"
复制代码
步骤5:测试运行
终端1 - 启动发送方:- echo "Hello P2P World!" > test.txt
- cargo run --bin sender
复制代码
复制输出的节点ID,然后在终端2执行:- cargo run --bin receiver --
复制代码
如果看到"文件下载完成!",恭喜你,P2P传输成功了!
四、进阶:实现实时聊天
iroh的 gossip 协议支持多播消息。核心代码片段:
- use iroh::gossip::Gossip;
- // 加入一个topic(任意32字节标识)
- let topic = [0u8; 32];
- let gossip = node.gossip();
- let sink = gossip.join(topic, vec![peer_id]).await?;
- // 发送消息
- sink.broadcast("你好,P2P世界!".into()).await?;
- // 接收消息(异步stream)
- while let Some(event) = sink.next().await {
- match event {
- Event::Received(msg) => {
- println!("收到: {}", String::from_utf8_lossy(&msg.content));
- }
- _ => {}
- }
- }
复制代码
五、常见问题
Q1:NAT穿透失败怎么办?
A:iroh内置了STUN/TURN支持,大部分情况下自动穿透。如果失败,可以配置中继服务器:- let node = Node::memory()
- .relay_mode(RelayMode::Custom(vec!["your-relay.com".parse()?]))
- .spawn().await?;
复制代码
Q2:内存节点和持久化节点有什么区别?
A:上面的示例用是内存存储,重启数据丢失。生产环境用- Node::persistent("./data")
复制代码 。
Q3:能传大文件吗?
A:可以。iroh的blobs协议支持分块传输,自动断点续传。测试过传10GB+的视频文件。
Q4:安全性如何?
A:所有连接强制TLS 1.3,节点身份基于Ed25519签名。密钥不泄露,中间人攻击理论上不可行。
六、总结
iroh代表了一种新思路:与其在复杂的IP地址、端口、DNS里打转,不如直接用加密密钥作为网络身份。这篇教程展示了最基础的文件传输,实际项目中你可以:
- 构建去中心化IM应用
- 实现分布式文件同步(类似Syncthing)
- 做IoT设备的点对点控制通道
- 搭建无需服务器的实时协作工具
Rust生态的P2P工具链正在成熟,iroh + libp2p + quinn的组合已经能撑起生产级应用。建议下一步阅读官方文档的 protocols 章节,了解blobs、gossip、documents三大核心协议。
参考资源:
有任何问题欢迎在楼下讨论,我会持续更新这篇教程。 |