说到网络,我们先复习一下ISO七层模型,
- 应用层
- 表现层
- 会话层
- 传输层
- 网络层
- 链路层
- 物理层
Rust标准库提供std::net 封装了TCP/IP协议栈。 tokio提供了高性能的异步网格。
先看下同步的std::net
std::net
std::net 下提供了处理 TCP / UDP 的数据结构,以及一些辅助结构:
- TCP:TcpListener / TcpStream,处理服务器的监听以及客户端的连接
- UDP:UdpSocket,处理 UDP socket
- 其它:IpAddr 是 IPv4 和 IPv6 地址的封装;SocketAddr,表示 IP 地址 端口的数据结构
TcpListener/TcpStream
对于服务端: 先创建一个TcpListener绑定端口, 再用loop循环 处理接收到的客户端请求。 收到请求后 会有TcpStream,实现了Read / Write trait。
代码语言:javascript复制use std::{
io::{Read, Write},
net::TcpListener,
thread,
};
fn main() {
let listener = TcpListener::bind("0.0.0.0:9527").unwrap();
loop {
let (mut stream, addr) = listener.accept().unwrap();
println!("Accepted a new connection: {}", addr);
thread::spawn(move || {
let mut buf = [0u8; 12];
stream.read_exact(&mut buf).unwrap();
println!("data: {:?}", String::from_utf8_lossy(&buf));
// 一共写了 17 个字节
stream.write_all(b"glad to meet you!").unwrap();
});
}
}
对于客户端: TcpStream::connect() 有一个 TcpStream。 然后再发送或接收数据。
处理网络连接的一般方法
循环accept 新连接,然后去异步处理这些请求的。 loop spawn 是处理网络连接的基本方式。 但是这种多线程处理,其实不可控。当请求量大,连接数就会多,导致线程数增加。加剧上下文切换的成本。
解决办法在 Rust 处理网络时,很少直接有用 std::net 进行处理的, 大部分都是用某个异步网络运行时,比如 tokio。 难怪我看很多 开源项目都用这个。
共享数据可以用channel, tokio里也有channel的实现。
处理网络数据的一般方法
我们自己新建的rust的数据结构, 通过serde 赋予了序列化跟反序列化,就是从rust的数据结构的文本形式到传输需要的文本形式的转化,或者反向转化,就可以形成json的数据类型了。