【Rust】【P2P】 简易P2P聊天室【TCP】

1 篇文章 0 订阅
1 篇文章 0 订阅

关于本项目

本项目是一个由Rust语言编写的终端简易P2P聊天室,基于TCP协议利用端口复用进行打洞,经测试在NAT1(Full Cone NAT,全锥形NAT)下完全可以。项目已发布到github(地址)。项目使用的基本都是一些基本库,未使用类似 libp2p 的现成的p2p库,使用的是tokio异步运行时框架(其实从0开始写也不是不可以,但是我非常不赞成重复造轮子的)。
声明:本项目只是个人练习项目,写得不是很好,大佬们轻喷

效果预览

Windows 10:
在这里插入图片描述
WSL2 和安卓
在这里插入图片描述 在这里插入图片描述
服务器日志
在这里插入图片描述

项目分析

项目分为三部分:网络传输、客户端、服务端。
内容比较多,我就挑一些重要的内容简单说一下。

客户端

客户端连接服务器:创建本地socket并绑定到随机端口,设置socket端口复用功能(这里有个坑,在linux下的TCP端口复用需要用TcpSocket::set_reuseport设置,而Windows不用。。)

let loc_addr = {
    let mut rng = rand::thread_rng();
    let port = rng.gen_range(4000..9000);
    SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port))
};
let mut server_stream = {
    let server_sock = TcpSocket::new_v4().unwrap();
    #[cfg(target_family = "unix")]
    {server_sock.set_reuseport(true).unwrap();}
    server_sock.set_reuseaddr(true).unwrap();
    server_sock.bind(loc_addr).unwrap();
    match server_sock.connect(server_addr.parse().unwrap()).await {
        Ok(sock) => { sock },
        Err(e) => {
            eprintln!("无法连接到服务器。{}", e);
            return;
        },
    }
};

加入房间,将socket绑定到连接服务端的地址和端口然后connect客户端就可以了。

// 接收服务端发送过来的所有房间内的peer
let clients: Vec<ClientInfo> = serde_json::from_slice(&net::read(&mut server_stream).await.unwrap()).unwrap();
...
for ci in clients {
    ...
    let mut stm = {
        let sock = TcpSocket::new_v4().unwrap();
        #[cfg(target_family = "unix")]
        {sock.set_reuseport(true).unwrap();}
        sock.set_reuseaddr(true).unwrap();
        sock.bind(addr.clone()).unwrap();
        if let Ok(stm) = sock.connect(ci.addr.clone()).await {
            stm
        } else {
            warn!("连接{:?}失败", &ci);
            return Err(ci);
        }
    };
    ...
    tokio::spawn(Process::new(
        &other, stm, msg_tx, cin_rx
    ).poll());
}

当有新的peer加入的时候服务端会将另一个peer的信息发送过来,这时候创建一个新的socket,设置端口复用,将socket绑定到连接服务端的地址和端口。最后connect新客户端就可以了。

let sock = TcpSocket::new_v4().unwrap();
#[cfg(target_family = "unix")]
{sock.set_reuseport(true).unwrap();}
sock.set_reuseaddr(true).unwrap();
if let Err(e) = sock.bind(addr.clone()) {
    error!("Fail to bind {} {}", &addr, e);
    continue;
};
let mut sock = {
    match sock.connect(ci.addr.clone()).await {
        Ok(s) => s,
        Err(e) => {
            warn!("Fail to connent {:?} : {}", theci, e);
            continue;
        }
    }
};

服务端

TODO

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值