rust 使用tokio实现socket多人聊天

首先我们实现多人聊天,首先要有服务端和客户端,
在这里插入图片描述

服务端只有一个,客户端有很多个,看上面的图,客户端1如果要给客户端2 3 4 发送一条消息,首先需要发送给服务端,然后转发给其余的3个客户端,明白了他们之前发送消息的流程,下面我们实现服务端,

//创建监听ip和端口
let addr = "127.0.0.1:5555".parse().unwrap();
//创建tcpSocket
let socket = TcpSocket::new_v4().unwrap();
//socket绑定地址
let _ = socket.bind(addr);
//socket开始监听
let listen = socket.listen(1024).unwrap();
//创建通道
let (tx,_rx) = tokio::sync::broadcast::channel(1024);

然后使用accept()等待客户端连接,客户端连接成功后处理消息和发送消息,因为是有很多个客户端所以使用loop循环进行监听
//等待客户端连接

let (mut tcp_stream,_) = listen.accept().await.unwrap();

如果有客户端连接到服务端,后面会处理消息和发送消息。

loop {
        //等待客户端的连接
        let (mut tcp_stream,_) = listen.accept().await.unwrap();

        let tx = tx.clone();
        let mut rx = tx.subscribe();
        //启动线程
        tokio::spawn(async move{
            //循环处理事件
            loop {
                //只要是返回一个就接结束等待的其他线程
                tokio::select! {
                    //处理消息
                    result = process(&mut tcp_stream,&tx) => {
                        //如果出现异常结束循环
                        if !result {
                            break;
                        }
                    }
                    //发送消息
                    result = rx.recv() => {
                        let (msg,addr) = result.unwrap();
                        //判断给除了自己的客户端发送消息
                        if addr != tcp_stream.peer_addr().unwrap(){
                            let _ = tcp_stream.write_all(msg.as_bytes()).await; 
                        } 
                    }
                }
            }
            //获取客户端地址
            let ip = tcp_stream.peer_addr().unwrap();
            println!("{:?}:断开连接",ip);
        });
        
    }
    ```
处理消息的方法
```rust
async fn process(tcp_stream:&mut TcpStream,tx: &Sender<(String,SocketAddr)>)-> bool{
    let (mut read_half,_write_half) = tcp_stream.split();
    
    let mut buf = vec![0;1024];
    
    //读取消息
    match read_half.read_buf(&mut buf).await {
        
        Ok(_n) => {
            //转换字符串
            let res = String::from_utf8(buf).unwrap();
            //println!("size:{},content:{}",n,res);
            let peer_addr = tcp_stream.peer_addr().unwrap();
            //通过通道发送
            tx.send((res,peer_addr)).unwrap();
            return true;
        }
        Err(err) => {   
            println!("err : {:?}",err);
            return false;
        }
    }
}

接下来编写客户端
客户端主要是接收服务端的消息和给服务端发送消息,我们给客户端开启一个线程用来接收消息,用loop循环给服务端发送消息。
首先是连接socket

let socket = TcpSocket::new_v4().unwrap();
let addr = "127.0.0.1:5555".parse().unwrap();
let mut tcp_stream = socket.connect(addr).await.unwrap();

连接socket成功后处理消息

//这个地方是因为下面线程中使用后他自动推导不出类型,所以在这个地方添加一个空的字符串
tx.send(String::new()).unwrap();
//开启线程处理消息和给服务端发送消息
tokio::spawn(async move {
    loop {
        let (mut read_half,mut write_half) = tcp_stream.split();
        let mut buf = vec![0;1024];
        tokio::select! {
            //读取服务端的消息
            result = read_half.read_buf(&mut buf) => {
                match result {
                    Ok(num) => {
                        //这个地方是因为每次接收到消息,总是会出现空消息,如果接收到不是空消息就不是1024大小
                        if num != 1024 {
                            //转换字符
                            let content = String::from_utf8(buf).unwrap();
                            //输出消息
                            print!("size = {},receive = {}",num,content);
                        }
                    }
                    Err(err) => {
                        println!("服务器连接断开!err={:?}",err);
                        //如果服务端断开则退出程序
                        std::process::exit(0);
                    }
                }

            }
            //读取通道的消息发送给服务端
            result = rx.recv() => {
                let send = result.unwrap();
                //发送给服务端
                let _ = write_half.write_all(send.as_bytes()).await;
                let _ = write_half.flush();
            }
        }
    }
});

loop循环读取控制台文字

loop {
    //创建string
    let mut send = String::new();
    //读取控制台文字
    std::io::stdin().read_line(&mut send).unwrap();
    //通过通道发送
    tx.send(send).unwrap();
}

完整的服务端代码

[dependencies]
tokio = { version = "1", features = ["full"] }
use std::net::SocketAddr;
use tokio::sync::broadcast::{Sender};

use tokio::net::{TcpSocket, TcpStream};
use tokio::io::{Error, AsyncReadExt, AsyncWriteExt};

async fn process(tcp_stream:&mut TcpStream,tx: &Sender<(String,SocketAddr)>)-> bool{
    let (mut read_half,_write_half) = tcp_stream.split();
    
    let mut buf = vec![0;1024];
    
    //读取消息
    match read_half.read_buf(&mut buf).await {
        
        Ok(_n) => {
            //转换字符串
            let res = String::from_utf8(buf).unwrap();
            //println!("size:{},content:{}",n,res);
            let peer_addr = tcp_stream.peer_addr().unwrap();
            //通过通道发送
            tx.send((res,peer_addr)).unwrap();
            return true;
        }
        Err(err) => {   
            println!("err : {:?}",err);
            return false;
        }
    }
}


#[tokio::main]
async fn main() -> Result<(),Error> {
    
    println!(r"
    _                                  
_ __ _   _ ___| |_   ___  ___ _ ____   _____ _ __ 
| '__| | | / __| __| / __|/ _ \ '__\ \ / / _ \ '__|
| |  | |_| \__ \ |_  \__ \  __/ |   \ V /  __/ |   
|_|   \__,_|___/\__| |___/\___|_|    \_/ \___|_|   
                                     ");

   

    let addr = "127.0.0.1:5555".parse().unwrap();

    let socket = TcpSocket::new_v4().unwrap();
    let _ = socket.bind(addr);
    let listen = socket.listen(1024).unwrap();

    println!("服务启动成功,端口:5555");

    let (tx,_rx) = tokio::sync::broadcast::channel(1024);

    loop {
        //等待客户端的连接
        let (mut tcp_stream,_) = listen.accept().await.unwrap();

        let tx = tx.clone();
        let mut rx = tx.subscribe();
        //启动线程
        tokio::spawn(async move{
            //循环处理事件
            loop {
                //只要是返回一个就接结束等待的其他线程
                tokio::select! {
                    //处理消息
                    result = process(&mut tcp_stream,&tx) => {
                        //如果出现异常结束循环
                        if !result {
                            break;
                        }
                    }
                    //发送消息
                    result = rx.recv() => {
                        let (msg,addr) = result.unwrap();
                        //判断给除了自己的客户端发送消息
                        if addr != tcp_stream.peer_addr().unwrap(){
                            let _ = tcp_stream.write_all(msg.as_bytes()).await; 
                        } 
                    }
                }
            }
            //获取客户端地址
            let ip = tcp_stream.peer_addr().unwrap();
            println!("{:?}:断开连接",ip);
        });
        
    }
}

完整的客户端代码

[dependencies]
tokio = { version = "1", features = ["full"] }

use tokio::io::AsyncReadExt;
use tokio::{net::TcpSocket, io::AsyncWriteExt};
use tokio::sync::broadcast::{self};


#[tokio::main]
async fn main() {

    println!(r"
    _          _ _            _   
_ __ _   _ ___| |_    ___| (_) ___ _ __ | |_ 
| '__| | | / __| __|  / __| | |/ _ \ '_ \| __|
| |  | |_| \__ \ |_  | (__| | |  __/ | | | |_ 
|_|   \__,_|___/\__|  \___|_|_|\___|_| |_|\__|");

    let socket = TcpSocket::new_v4().unwrap();
    let addr = "127.0.0.1:5555".parse().unwrap();
    let mut tcp_stream = socket.connect(addr).await.unwrap();
    
    println!("连接成功:{:?}",addr);

    let (tx,mut rx) = broadcast::channel(1024);
    
    //这个地方是因为下面线程中使用后他自动推导不出类型,所以在这个地方添加一个空的字符串
    tx.send(String::new()).unwrap();
    //开启线程处理消息和给服务端发送消息
    tokio::spawn(async move {
        loop {
            let (mut read_half,mut write_half) = tcp_stream.split();
            let mut buf = vec![0;1024];
            tokio::select! {
                //读取服务端的消息
                result = read_half.read_buf(&mut buf) => {
                    match result {
                        Ok(num) => {
                            //这个地方是因为每次接收到消息,总是会出现空消息,如果接收到不是空消息就不是1024大小
                            if num != 1024 {
                                //转换字符
                                let content = String::from_utf8(buf).unwrap();
                                //输出消息
                                print!("size = {},receive = {}",num,content);
                            }
                        }
                        Err(err) => {
                            println!("服务器连接断开!err={:?}",err);
                            //如果服务端断开则退出程序
                            std::process::exit(0);
                        }
                    }
                    
                }
                //读取通道的消息发送给服务端
                result = rx.recv() => {
                    let send = result.unwrap();
                    //发送给服务端
                    let _ = write_half.write_all(send.as_bytes()).await;
                    let _ = write_half.flush();
                }
            }
        }
    });
    loop {
        //创建string
        let mut send = String::new();
        //读取控制台文字
        std::io::stdin().read_line(&mut send).unwrap();
        //通过通道发送
        tx.send(send).unwrap();
    }
}

github-client:socket-client
github-server:socket-server

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若梦网络编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值