首先我们实现多人聊天,首先要有服务端和客户端,
服务端只有一个,客户端有很多个,看上面的图,客户端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