Rust 系统编程核心技能——文件操作与网络编程基础及进阶
文章介绍了 Rust 系统编程中的文件系统操作与网络通信基础。内容涵盖标准库 std::fs 的文件创建、读写、删除及目录遍历,Unix 权限管理;TCP/IP 协议栈原理,单线程与多线程 TCP 服务器实现;以及基于 Tokio 库的异步网络编程模型。通过本地日志分析工具和异步 HTTP 客户端等实战案例,展示了 Rust 在系统级数据处理与通信中的应用,并总结了资源泄漏、阻塞 IO 等常见问题及解决方案。

文章介绍了 Rust 系统编程中的文件系统操作与网络通信基础。内容涵盖标准库 std::fs 的文件创建、读写、删除及目录遍历,Unix 权限管理;TCP/IP 协议栈原理,单线程与多线程 TCP 服务器实现;以及基于 Tokio 库的异步网络编程模型。通过本地日志分析工具和异步 HTTP 客户端等实战案例,展示了 Rust 在系统级数据处理与通信中的应用,并总结了资源泄漏、阻塞 IO 等常见问题及解决方案。

std::fs 模块的文件创建、读写、删除、重命名,目录遍历、权限管理(Unix 系统为主),以及错误处理tokio 库)、连接池、超时设置,避免常见的性能陷阱(如阻塞 IO 导致的响应慢)openssl 或 rustls 库),防止数据在传输过程中被窃取或篡改💡 三大核心难点:
tokio 库的 async/await 语法组织异步代码,理解任务调度和 Future 的执行过程⚠️ 三大高频错误点:
Rust 标准库 std::fs 模块提供了完整的文件系统操作 API,包括文件的创建、读写、删除、重命名,目录的遍历、权限管理等。
⌨️ 文件的创建与写入示例:
use std::fs::File;
use std::io::{Write, BufWriter};
use std::path::PathBuf;
fn write_to_file(file_path: PathBuf, content: &str) -> Result<(), Box<dyn std::error::Error>> {
// 方法 1:直接创建文件并写入(覆盖原有内容)
let mut file = File::create(&file_path)?;
file.write_all(content.as_bytes())?;
file.flush()?; // 确保所有数据写入磁盘
println!("文件写入成功:{:?}", file_path);
// 方法 2:使用 BufWriter 缓冲写入(适合大量数据)
let mut file = File::create(PathBuf::from("output_buffered.txt"))?;
let mut buf_writer = BufWriter::new(file);
buf_writer.write_all(b"Hello, ")?;
buf_writer.write_all(b"Rust! ")?;
buf_writer.write_all(b"Buffered write example")?;
buf_writer.flush()?; // 必须手动刷新缓冲区
println!("缓冲写入成功:output_buffered.txt");
// 方法 3:追加写入
let mut file = File::options().append(true).open(PathBuf::from("output_buffered.txt"))?;
file.write_all(b"\nAppended content")?;
println!("追加写入成功:output_buffered.txt");
Ok(())
}
fn main() {
let file_path = PathBuf::from("output.txt");
let content = "Hello, Rust! This is a test file.\n";
if let Err(e) = write_to_file(file_path, content) {
println!("错误:{}", e);
}
}
⌨️ 文件的读取示例:
use std::fs::File;
use std::io::{Read, BufReader};
use std::path::PathBuf;
fn read_from_file(file_path: PathBuf) -> Result<String, Box<dyn std::error::Error>> {
// 方法 1:直接读取整个文件
let mut content = String::new();
let mut file = File::open(&file_path)?;
file.read_to_string(&mut content)?;
println!("直接读取成功:{:?}", file_path);
// 方法 2:使用 BufReader 缓冲读取(适合大量数据)
let file = File::open(PathBuf::from("output_buffered.txt"))?;
let mut buf_reader = BufReader::new(file);
let mut line_count = 0;
loop {
let mut line = String::new();
let bytes_read = buf_reader.read_line(&mut line)?;
if bytes_read == 0 {
break;
}
line_count += 1;
print!("第{}行:{}", line_count, line.trim());
}
println!("\n缓冲读取成功:output_buffered.txt");
Ok(content)
}
fn main() {
let file_path = PathBuf::from("output.txt");
if let Ok(content) = read_from_file(file_path) {
println!("文件内容:{}", content);
} else {
println!("错误:文件不存在");
}
}
⌨️ 文件的删除与重命名示例:
use std::fs;
use std::path::PathBuf;
fn file_operations() -> Result<(), Box<dyn std::error::Error>> {
// 重命名文件
fs::rename("output.txt", "renamed_output.txt")?;
println!("文件重命名成功:renamed_output.txt");
// 删除文件
fs::remove_file("output_buffered.txt")?;
println!("文件删除成功:output_buffered.txt");
Ok(())
}
fn main() {
if let Err(e) = file_operations() {
println!("错误:{}", e);
}
}
⌨️ 目录的创建与删除示例:
use std::fs;
use std::path::PathBuf;
fn dir_operations() -> Result<(), Box<dyn std::error::Error>> {
// 创建单个目录
fs::create_dir("test_dir")?;
println!("单个目录创建成功:test_dir");
// 创建嵌套目录
fs::create_dir_all("test_dir/nested_dir/sub_dir")?;
println!("嵌套目录创建成功:test_dir/nested_dir/sub_dir");
// 删除空目录
fs::remove_dir("test_dir/nested_dir/sub_dir")?;
println!("空目录删除成功:test_dir/nested_dir/sub_dir");
// 删除目录树(递归删除)
fs::remove_dir_all("test_dir")?;
println!("目录树删除成功:test_dir");
Ok(())
}
fn main() {
if let Err(e) = dir_operations() {
println!("错误:{}", e);
}
}
⌨️ 目录的遍历示例:
use std::fs;
use std::path::PathBuf;
fn traverse_dir(dir_path: PathBuf, level: usize) -> Result<(), Box<dyn std::error::Error>> {
let prefix = " ".repeat(level);
for entry in fs::read_dir(dir_path)? {
let entry = entry?;
let file_type = entry.file_type()?;
let file_name = entry.file_name().into_string().map_err(|_| "文件名不是 UTF-8 编码")?;
if file_type.is_dir() {
println!("{}📁 {}", prefix, file_name);
traverse_dir(entry.path(), level + 1)?;
} else if file_type.is_file() {
println!("{}📄 {}", prefix, file_name);
} else if file_type.is_symlink() {
println!("{}🔗 {}", prefix, file_name);
}
}
Ok(())
}
fn main() {
let dir_path = PathBuf::from("."); // 当前目录
println!("当前目录结构:");
if let Err(e) = traverse_dir(dir_path, 0) {
println!("错误:{}", e);
}
}
Rust 标准库 std::os::unix::fs 模块提供了Unix 系统特有的文件权限管理 API,包括权限位的设置与获取。
⌨️ 文件权限管理示例(Unix 系统):
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
fn permissions_operations() -> Result<(), Box<dyn std::error::Error>> {
let file_path = PathBuf::from("test_permissions.txt");
fs::write(&file_path, "Test permissions")?;
// 获取当前权限
let mut permissions = fs::metadata(&file_path)?.permissions();
println!("当前权限位:{:o}", permissions.mode());
// 设置权限(例如,只有所有者可读写)
permissions.set_mode(0o600);
fs::set_permissions(&file_path, permissions)?;
println!("修改后的权限位:{:o}", fs::metadata(&file_path)?.permissions().mode());
fs::remove_file(file_path)?;
Ok(())
}
fn main() {
if let Err(e) = permissions_operations() {
println!("错误:{}", e);
}
}
Rust 标准库 std::net 模块提供了TCP/IP 协议栈的基础 API,包括 TCP 服务器/客户端的连接建立、数据收发等。
⌨️ 简单 TCP 客户端示例:
use std::io::{Read, Write};
use std::net::TcpStream;
use std::str;
fn tcp_client() -> Result<(), Box<dyn std::error::Error>> {
// 连接到服务器
let mut stream = TcpStream::connect("127.0.0.1:8080")?;
println!("成功连接到服务器:127.0.0.1:8080");
// 发送数据
let message = "Hello, Rust TCP Server!";
stream.write_all(message.as_bytes())?;
println!("发送数据:{}", message);
// 读取服务器响应
let mut buffer = [0; 1024];
let bytes_read = stream.read(&mut buffer)?;
let response = str::from_utf8(&buffer[..bytes_read])?;
println!("收到服务器响应:{}", response);
Ok(())
}
fn main() {
if let Err(e) = tcp_client() {
println!("错误:{}", e);
}
}
⌨️ 简单 TCP 服务器示例(单线程):
use std::io::{Read, Write};
use std::net::TcpListener;
fn handle_client(mut stream: std::net::TcpStream) -> Result<(), Box<dyn std::error::Error>> {
let peer_addr = stream.peer_addr()?;
println!("新客户端连接:{}", peer_addr);
let mut buffer = [0; 1024];
loop {
let bytes_read = stream.read(&mut buffer)?;
if bytes_read == 0 {
println!("客户端断开连接:{}", peer_addr);
break;
}
let message = String::from_utf8_lossy(&buffer[..bytes_read]);
println!("收到客户端{}的消息:{}", peer_addr, message.trim());
// 发送响应
let response = format!("服务器收到您的消息:{}", message.trim());
stream.write_all(response.as_bytes())?;
}
Ok(())
}
fn tcp_server() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("服务器启动成功,监听地址:127.0.0.1:8080");
// 单线程处理连接(一次只能处理一个连接)
for stream in listener.incoming() {
match stream {
Ok(stream) => {
if let Err(e) = handle_client(stream) {
println!("处理客户端连接错误:{}", e);
}
}
Err(e) => {
println!("接受连接错误:{}", e);
}
}
}
Ok(())
}
fn main() {
if let Err(e) = tcp_server() {
println!("服务器错误:{}", e);
}
}
单线程服务器无法处理大量并发连接,我们可以使用多线程模型来解决这个问题。
⌨️ 多线程 TCP 服务器示例:
use std::io::{Read, Write};
use std::net::TcpListener;
use std::thread;
fn handle_client(mut stream: std::net::TcpStream) -> Result<(), Box<dyn std::error::Error>> {
let peer_addr = stream.peer_addr()?;
println!("新客户端连接:{}", peer_addr);
let mut buffer = [0; 1024];
loop {
let bytes_read = stream.read(&mut buffer)?;
if bytes_read == 0 {
println!("客户端断开连接:{}", peer_addr);
break;
}
let message = String::from_utf8_lossy(&buffer[..bytes_read]);
println!("收到客户端{}的消息:{}", peer_addr, message.trim());
let response = format!("服务器收到您的消息:{}", message.trim());
stream.write_all(response.as_bytes())?;
}
Ok(())
}
fn tcp_server() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("服务器启动成功,监听地址:127.0.0.1:8080");
// 多线程处理连接
for stream in listener.incoming() {
match stream {
Ok(stream) => {
// 每个连接创建一个新线程
thread::spawn(move || {
if let Err(e) = handle_client(stream) {
println!("处理客户端连接错误:{}", e);
}
});
}
Err(e) => {
println!("接受连接错误:{}", e);
}
}
}
Ok(())
}
fn main() {
if let Err(e) = tcp_server() {
println!("服务器错误:{}", e);
}
}
Rust 的标准库网络编程 API 是阻塞式的,无法满足高并发场景的需求。我们可以使用异步网络编程库 Tokio来实现高性能的网络应用程序。
Tokio 是 Rust 中最流行的异步运行时库,它提供了异步 IO、定时器、任务调度等功能,支持 async/await 语法。
在 Cargo.toml 中添加 Tokio 库的依赖:
[dependencies]
tokio = { version = "1", features = ["full"] }
⌨️ 异步 TCP 客户端示例(Tokio 库):
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn tcp_client() -> Result<(), Box<dyn std::error::Error>> {
// 连接到服务器
let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
println!("成功连接到服务器:127.0.0.1:8080");
// 发送数据
let message = "Hello, Rust Async TCP Server!";
stream.write_all(message.as_bytes()).await?;
println!("发送数据:{}", message);
// 读取服务器响应
let mut buffer = [0; 1024];
let bytes_read = stream.read(&mut buffer).await?;
let response = String::from_utf8_lossy(&buffer[..bytes_read]);
println!("收到服务器响应:{}", response);
Ok(())
}
fn main() {
if let Err(e) = tcp_client() {
println!("错误:{}", e);
}
}
⌨️ 异步 TCP 服务器示例(Tokio 库):
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
async fn handle_client(mut stream: tokio::net::TcpStream) -> Result<(), Box<dyn std::error::Error>> {
let peer_addr = stream.peer_addr()?;
println!("新客户端连接:{}", peer_addr);
let mut buffer = [0; 1024];
loop {
let bytes_read = stream.read(&mut buffer).await?;
if bytes_read == 0 {
println!("客户端断开连接:{}", peer_addr);
break;
}
let message = String::from_utf8_lossy(&buffer[..bytes_read]);
println!("收到客户端{}的消息:{}", peer_addr, message.trim());
let response = format!("服务器收到您的消息:{}", message.trim());
stream.write_all(response.as_bytes()).await?;
}
Ok(())
}
#[tokio::main]
async fn tcp_server() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("服务器启动成功,监听地址:127.0.0.1:8080");
// 异步处理连接
loop {
let (stream, _) = listener.accept().await?;
// 每个连接创建一个新任务
tokio::spawn(async move {
if let Err(e) = handle_client(stream).await {
println!("处理客户端连接错误:{}", e);
}
});
}
}
fn main() {
if let Err(e) = tcp_server() {
println!("服务器错误:{}", e);
}
}
💡 场景分析:需要编写一个本地日志分析工具,支持统计指定目录下所有日志文件中特定关键词出现的次数,输出统计结果。
⌨️ 代码示例:
use std::collections::HashMap;
use std::fs;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
fn count_keywords_in_dir(dir_path: PathBuf, keywords: &[String]) -> Result<HashMap<String, u32>, Box<dyn std::error::Error>> {
let mut keyword_count = HashMap::new();
for keyword in keywords {
keyword_count.insert(keyword.clone(), 0);
}
for entry in fs::read_dir(dir_path)? {
let entry = entry?;
let file_type = entry.file_type()?;
if file_type.is_file() && entry.file_name().to_str().map_or(false, |name| name.ends_with(".log")) {
let file_path = entry.path();
let file = fs::File::open(file_path)?;
let reader = BufReader::new(file);
for line_result in reader.lines() {
let line = line_result?;
for keyword in keywords {
let count = line.matches(keyword).count();
*keyword_count.get_mut(keyword).unwrap() += count as u32;
}
}
}
}
Ok(keyword_count)
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 3 {
println!("Usage: cargo run <dir_path> <keyword1> <keyword2> ...");
return;
}
let dir_path = PathBuf::from(&args[1]);
let keywords = &args[2..];
if let Ok(keyword_count) = count_keywords_in_dir(dir_path, keywords) {
println!("关键词统计结果:");
println!("----------");
for (keyword, count) in keyword_count.iter() {
println!("{:<20} {}", keyword, count);
}
} else {
println!("错误:目录不存在或无法访问");
}
}
💡 场景分析:需要编写一个简单的异步 HTTP 客户端请求工具,支持发送 GET 和 POST 请求,输出响应状态码和内容。
在 Cargo.toml 中添加 reqwest 库的依赖(reqwest 是基于 Tokio 的异步 HTTP 客户端库):
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
⌨️ 代码示例:
use reqwest::Client;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
println!("Usage: cargo run <url> [method] [json_body]");
println!(" method: GET (default) or POST");
println!(" json_body: JSON string for POST request");
return Ok(());
}
let url = &args[1];
let method = args.get(2).map_or("GET", |s| s.to_uppercase().as_str());
let json_body = args.get(3);
let client = Client::new();
let response = match method {
"GET" => client.get(url).send().await?,
"POST" => {
if let Some(body) = json_body {
let json_value: Value = serde_json::from_str(body)?;
client.post(url).json(&json_value).send().await?
} else {
println!("错误:POST 请求需要提供 JSON body");
return Ok(());
}
}
_ => {
println!("错误:不支持的 HTTP 方法");
return Ok(());
}
};
println!("响应状态码:{}", response.status());
println!("响应内容类型:{:?}", response.headers().get("content-type"));
println!("响应内容:{}", response.text().await?);
Ok(())
}
问题现象:导致资源泄漏。
解决方案:
std::mem::forget 函数drop 函数问题现象:导致程序无法处理其他请求。
解决方案:
AsyncRead、AsyncWrite trait)std::io::Read、std::io::Write trait)tokio::task::spawn_blocking 函数问题现象:在读写网络数据时未正确处理缓冲区的大小。
解决方案:
Vec<u8>)来存储大量数据✅ 掌握了文件系统操作:熟练运用了标准库 std::fs 模块的文件创建、读写、删除、重命名,目录遍历、权限管理,以及错误处理
✅ 精通了网络通信基础:理解了 TCP/IP 协议栈的基础原理,学习了 TCP 服务器/客户端的完整实现流程,包括单线程和多线程模型
✅ 优化了网络性能:深入了解了 Rust 网络编程中的异步通信模型(结合 tokio 库),理解了任务调度和 Future 的执行过程
✅ 实战系统编程:结合真实场景编写了两个实用的代码案例:本地日志分析工具和简单异步 HTTP 客户端请求工具
✅ 了解了网络安全基础:学习了使用 reqwest 库发送 HTTPS 请求的方法(reqwest 默认启用 SSL/TLS 加密)
下一篇文章,我们将深入学习 Rust 的错误处理与测试,包括自定义错误类型的实现、错误传播的方法、单元测试的编写、集成测试的实现,通过这些知识我们将能够编写更健壮、更可维护的应用程序。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online