Rust Web 全栈开发(一):构建 TCP Server
Rust Web 全栈开发(一):构建 TCP Server
- Rust Web 全栈开发(一):构建 TCP Server
- 标准库的 std::net 模块
- TCP 通信
- UDP 通信
- 地址表示
- 新建项目
- 建立连接
- 收发数据
Rust Web 全栈开发(一):构建 TCP Server
参考视频:https://www.bilibili.com/video/BV1RP4y1G7KF
标准库的 std::net 模块
std::net 是 Rust 标准库中提供基础、跨平台网络 I/O 功能的核心模块。它主要处理 TCP 和 UDP 协议的通信,提供了同步(阻塞式)的网络操作接口。它是构建网络应用程序的基础,通常用于实现服务器、客户端以及点对点通信。
TCP 通信
TcpStream 代表一个 TCP 连接(客户端或服务器端已建立的连接),提供读写功能。
TcpListener 代表一个 TCP 服务器套接字,绑定到特定地址和端口,监听连接请求。
服务器创建监听套接字,接受来自客户端的连接请求。accept() 返回一个 TcpStream 用于与特定客户端通信。
UDP 通信
UdpSocket 代表一个 UDP 套接字,提供无连接的数据报发送和接收。消息是离散的,不保证顺序、可靠性和到达。
用途:DNS 查询、实时音视频流、游戏状态更新、广播/组播等场景,对延迟敏感但能容忍少量丢包。
地址表示
std::net 提供了表示网络地址的关键类型:IpAddr、Ipv4Addr、Ipv6Addr、SocketAddr,等等。
新建项目
首先用 RustRover 新建一个二进制项目。
修改 Cargo.toml 文件,配置为工作区:
[workspace]
然后再在该目录下新建两个成员包:tcpserver、tcpclient。
cargo new tcpserver
cargo new tcpclient
在工作区内运行 cargo new 会自动将新创建的包添加到工作区内 Cargo.toml 的 [workspace] 定义中的 members 键中,如下所示:
此时,我们可以通过运行 cargo build 来构建工作区。项目目录下的文件应该是这样的:
├── Cargo.lock
├── Cargo.toml
├── tcpclient
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── tcpserver
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
更多关于工作区的知识,请参考:Rust 学习笔记:Cargo 工作区
建立连接
打开 tcpserver 成员包下的 main.rs,编写代码:
use std::net::TcpListener;fn main() {let listener = TcpListener::bind("127.0.0.1:3000").unwrap();println!("Running on port 3000 ...");for stream in listener.incoming() {let stream = stream.unwrap();println!("Connection established!");}
}
程序创建了一个监听套接字 listener,监听本地环回地址的 3000 端口。incoming() 方法返回一个迭代器,方便处理多个连接。stream 是一个 TcpStream,可以用于读写数据。
打开 tcpclient 成员包下的 main.rs,编写代码:
use std::net::TcpStream;fn main() {let stream = TcpStream::connect("localhost:3000").unwrap();
}
程序作为客户端发起连接,连接到本地环回地址的 3000 端口。
在终端中输入命令: cargo run -p tcpserver,运行服务端进行监听。
新建一个终端,在新的终端中输入命令: cargo run -p tcpclient,运行客户端发起连接。
回到服务端,新打印了一条语句:
说明服务端监听到了来自客户端的请求,并成功建立了连接。
客户端发起连接后,main 函数就执行完了。但 incoming() 方法不会只接收一次连接就关闭,所以服务端要用 Ctrl + C 强制终止程序。
收发数据
TcpStream 实现了 io::Write、io::Read 两个 trait。
打开 tcpserver 成员包下的 main.rs,修改代码:
use std::io::{Read, Write};
use std::net::TcpListener;fn main() {let listener = TcpListener::bind("127.0.0.1:3000").unwrap();println!("Running on port 3000 ...");for stream in listener.incoming() {let mut stream = stream.unwrap();println!("Connection established!");let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();stream.write(&mut buffer).unwrap();}
}
服务端建立连接后,读取 stream 中接收到的内容,存储在 buffer 中。再将 buffer 通过 stream 发送给客户端。
这种从客户端读取内容,再原封不动发送给相同客户端的服务端,一般称为回声(echo)服务端。
打开 tcpclient 成员包下的 main.rs,修改代码:
use std::io::{Read, Write};
use std::net::TcpStream;fn main() {let mut stream = TcpStream::connect("localhost:3000").unwrap();stream.write("Hello, World!".as_bytes()).unwrap();let mut buffer = [0; 13];stream.read(&mut buffer).unwrap();println!("Response from server: {:?}", str::from_utf8(&buffer).unwrap());
}
客户端发送一个字符串给 localhost:3000,再从 stream 中读取内容,并打印出来。
和上一小节一样,在终端中输入命令: cargo run -p tcpserver,运行服务端进行监听。新建一个终端,在新的终端中输入命令: cargo run -p tcpclient,运行客户端发起连接。
可以看出,服务端监听端口,实现了回声功能。客户端最终接收到了自己发给客户端的信息。