一、前言
网络开发是各种语言都绕不开的话题,所以本章就来详细介绍一下rust语言网络开发的技巧。
如果你还不理解网络的基本工作原理,可以先参考文章:网络编程。
二、TCP编程
首先依旧是TCP协议,相比于C++,在rust中进行网络编程就要简单的多了,因为其默认封装了大量的细节。
比如一个最简单的TCP回声服务器写法如下:
use std::{
io::{Read, Write},
net::TcpListener,
};
fn main() {
let listener = TcpListener::bind("127.0.0.1:8888").unwrap();
for cli in listener.incoming() {
let Ok(mut cli) = cli else {
println!("error:{:?}", cli.unwrap_err());
continue;
};
std::thread::spawn(move || loop {
let mut buf = [0; 1024]; //申请接收数据的缓冲区
let ret = cli.read(&mut buf); //开始接受数据
if let Err(e) = ret {
println!("接收数据失败:{}", e);
break;
};
println!(
"接收到来自客户端的数据:{}",
String::from_utf8(buf.to_vec()).unwrap() //使用from_utf8将字节数据转换为字符串进行输出。
);
let ret = cli.write_all(&buf); //将收到的数据重新发送给客户端
if let Err(e) = ret {
println!("发送数据失败:{}", e);
break;
}
});
}
}
由于rust已经为我们封装了大量的底层细节,所以这里就简单理一下这段代码的工作逻辑即可。
- 首先使用
TcpListener
上的bind
方法绑定地址、端口,然后返回一个tcp服务器实例。 - 紧接着调用该实例上的
incoming
函数获取连接到该服务上的客户端实例迭代器,如果暂时没有客户端连接上来,那么程序会卡在这里。 - 然后来到循环内部,为每个连接上来的客户端都开启一个线程进入loop循环不断处理其数据。
read
方法可以接收客户端发送来的字节,write_all
则用于向客户端发送数据。
所以最后就是直接将客户端发送来的数据直接发送回去,实现一个“回声服务器”的效果。
然后是客户端代码,就更简单了:
use std::{
io::{Read, Write},
net::TcpStream,
};
fn main() {
let mut cli = TcpStream::connect("127.0.0.1:8888").unwrap();
loop {
let mut buf = String::new();
std::io::stdin().read_line(&mut buf).unwrap(); //接受命令行输入的文本内容,每次读取一行
let buf: &[u8] = &buf.into_bytes(); //转换为字节数据
let ret = cli.write_all(buf); //发送字节数据
if let Err(e) = ret {
println!("发送数据失败:{}", e);
break;
}
let mut buf = [0; 1024];
let ret = cli.read(&mut buf); //接收服务器返回的数据
if let Err(e) = ret {
println!("接收数据失败:{}", e);
break;
}
println!(
"接收到来自服务器的数据:{}",
String::from_utf8(buf.to_vec()).unwrap()
);
}
}
下面是简单的代码逻辑分析:
- 首先使用
TcpStream
内的connect
函数连接服务器,得到一个可以与服务器收发数据的实例。 - 然后使用
loop
无限循环,每次循环都从控制台读取一行用户输入的内容发送给服务器 - 最后再从服务器接收数据,并将其转换为字符串打印出来。
然后将上面两份代码分别编译,或者你也可以直接建两个rust项目,分别同时运行。