【Rust TCP编程】Rust网络编程之TCP编程语法解析与应用实战
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在Rust中进行TCP编程前,我们需要了解一些基本概念:- 面向连接:通信双方必须先建立连接才能传输数据- 可靠性:TCP保证数据按序到达,无差错、不丢失、不重复- 全双工:连接双方可以同时发送和接收数据- 流量控制:防止发送方发送过快导致接收方来不及处理- 拥塞控制:防止网络过载TCP使用IP地址和端口号来标识通信端点,
✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:Rust语言通关之路
景天的主页:景天科技苑
文章目录
Rust TCP编程
Rust是一种系统编程语言,专注于安全、并发和性能。它提供了零成本抽象、内存安全和线程安全等特性,使其成为网络编程的理想选择。
本文将详细介绍如何在Rust中进行TCP网络编程,涵盖从基础概念到实际应用的各个方面。
1. TCP协议基础
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在Rust中进行TCP编程前,我们需要了解一些基本概念:
- 面向连接:通信双方必须先建立连接才能传输数据
- 可靠性:TCP保证数据按序到达,无差错、不丢失、不重复
- 全双工:连接双方可以同时发送和接收数据
- 流量控制:防止发送方发送过快导致接收方来不及处理
- 拥塞控制:防止网络过载
TCP使用IP地址和端口号来标识通信端点,通常表示为IP:Port的形式。
2. Rust网络编程概览
Rust标准库提供了强大的网络编程支持,主要位于std::net模块中。对于TCP编程,我们需要关注以下几个关键类型:
- TcpListener:用于监听TCP连接的服务器端组件
- TcpStream:表示一个TCP连接的双向流
- SocketAddr:表示IP地址和端口号的组合
此外,Rust的异步网络编程通常使用tokio或async-std等异步运行时,但本文主要关注同步编程模型。
3. 创建TCP服务器
3.1 基本服务器
让我们从创建一个简单的TCP服务器开始,它监听本地端口8080并接受连接:
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
//定义处理客户端连接的函数
fn handle_client(mut stream: TcpStream) {
//读取客户端发送的数据,每次读取512字节
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
//打印接收到的数据
println!("Received: {}", String::from_utf8_lossy(&buffer[..]));
//向客户端发送响应
let response = b"HTTP/1.1 200 OK\r\n\r\nHello from server!";
stream.write(response).unwrap();
//刷新缓冲区,确保数据被发送
stream.flush().unwrap();
}
fn main() -> std::io::Result<()> {
//绑定到指定地址和端口,创建TCP监听器
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Server listening on port 8080");
//循环处理客户端连接
for stream in listener.incoming() {
match stream {
Ok(stream) => {
handle_client(stream);
}
Err(e) => {
println!("Connection failed: {}", e);
}
}
}
Ok(())
}
3.2 代码解析
TcpListener::bind()创建一个新的TCP监听器,绑定到指定的地址和端口
listener.incoming()返回一个迭代器,产生连接的TcpStream对象
handle_client函数处理每个连接:
- 创建一个缓冲区来读取数据
- 从流中读取数据并打印
- 写回一个简单的HTTP响应
3.3 改进服务器
上面的服务器只能处理一个连接,让我们改进它以处理多个连接:
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
use std::thread;
//定义处理客户端连接的函数
fn handle_client(mut stream: TcpStream) {
//读取客户端发送的数据,每次读取512字节
let mut buffer = [0; 512];
//循环读取数据
loop {
let bytes_read = stream.read(&mut buffer).unwrap();
if bytes_read == 0 {
//如果客户端关闭连接,则退出循环
break;
}
//打印接收到的数据
println!("Received: {}", String::from_utf8_lossy(&buffer[..]));
//向客户端发送响应
let response = b"HTTP/1.1 200 OK\r\n\r\nHello from server!";
stream.write(response).unwrap();
//刷新缓冲区,确保数据被发送
stream.flush().unwrap();
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Server listening on port 8080");
//创建一个容器,用于存储线程句柄
let mut handlers = Vec::new();
//循环处理客户端连接
for stream in listener.incoming() {
match stream {
Ok(stream) => {
//为每个客户端连接创建一个新的线程
let handler = thread::spawn(move || {
handle_client(stream);
});
handlers.push(handler);
}
Err(e) => {
println!("Connection failed: {}", e);
}
}
}
//等待所有线程结束
for handler in handlers {
handler.join().unwrap();
}
Ok(())
}
现在每个连接都在单独的线程中处理,服务器可以同时服务多个客户端。
4. 创建TCP客户端
4.1 基本客户端
让我们创建一个简单的TCP客户端来连接我们的服务器:
use std::net::TcpStream;
use std::io::{ Read, Write };
fn main() -> std::io::Result<()> {
//连接到服务器
let mut stream = TcpStream::connect("127.0.0.1:8080")?;
println!("Connected to server!");
//向服务器发送数据
let message = b"Hello from client!";
stream.write(message)?;
stream.flush()?;
//读取服务器的响应
let mut buffer = [0; 512];
stream.read(&mut buffer)?;
println!("Received: {}", String::from_utf8_lossy(&buffer[..]));
Ok(())
}
4.2 代码解析
TcpStream::connect()尝试连接到指定的服务器地址
write()方法发送数据到服务器
read()方法从服务器读取响应数据
4.3 改进客户端
让我们改进客户端以发送多行文本并保持连接:
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
use std::thread;
//定义处理客户端连接的函数
fn handle_client(mut stream: TcpStream) {
//读取客户端发送的数据,每次读取512字节
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
//打印接收到的数据
println!("Received: {}", String::from_utf8_lossy(&buffer[..]));
//向客户端发送响应
let response = b"HTTP/1.1 200 OK\r\n\r\nHello from server!";
stream.write(response).unwrap();
//刷新缓冲区,确保数据被发送
stream.flush().unwrap();
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Server listening on port 8080");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
//为每个客户端连接创建一个新的线程
thread::spawn(|| {
handle_client(stream);
});
}
Err(e) => {
println!("Connection failed: {}", e);
}
}
}
Ok(())
}
5. 基于tcp的大文件传输
我们仍使用标准库,并处理大文件(>1GB)时的流式传输。
u64 可支持 最大 16EB(远超文件系统限制)。
流式处理确保低内存使用
- 高效:避免一次性读入大文件,占用大量内存。
- 稳定:处理断点、部分传输、TCP粘包。
- 可靠:尽量传完全部字节,确保不丢包。
服务器:接收大文件(server.rs)
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
use std::fs::File;
use std::thread;
use std::path::PathBuf;
fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
let mut len_buf = [0; 2];
stream.read_exact(&mut len_buf)?;
let name_len = u16::from_be_bytes(len_buf) as usize;
let mut name_buf = vec![0u8; name_len];
stream.read_exact(&mut name_buf)?;
let filename = String::from_utf8_lossy(&name_buf).to_string();
let mut size_buf = [0; 8];
stream.read_exact(&mut size_buf)?;
let file_size = u64::from_be_bytes(size_buf);
println!("📥 接收大文件:{} ({} bytes)", filename, file_size);
//创建保存文件的目录
let mut path = PathBuf::from("received");
std::fs::create_dir_all(&path)?;
path.push(&filename);
let mut file = File::create(path)?;
let mut received: u64 = 0;
let mut buffer = [0; 8192];
//循环接收文件内容
while received < file_size {
let to_read = std::cmp::min(buffer.len() as u64, file_size - received) as usize;
let n = stream.read(&mut buffer[..to_read])?;
if n == 0 {
break;
}
file.write_all(&buffer[..n])?;
received += n as u64;
}
println!("✅ 文件接收完成,总计:{} bytes", received);
Ok(())
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("0.0.0.0:7878")?;
println!("🌐 等待客户端连接...");
for stream in listener.incoming() {
let stream = stream?;
thread::spawn(|| {
if let Err(e) = handle_client(stream) {
eprintln!("❌ 错误:{}", e);
}
});
}
Ok(())
}
客户端:发送大文件(client.rs)
use std::net::TcpStream;
use std::io::{ Read, Write };
use std::fs::File;
use std::path::Path;
fn main() -> std::io::Result<()> {
let filepath = "VMware-Workstation.zip"; // 可以是任意大文件
let path = Path::new(filepath);
let filename = path.file_name().unwrap().to_str().unwrap();
let mut file = File::open(path)?;
let file_size = file.metadata()?.len();
let mut stream = TcpStream::connect("127.0.0.1:7878")?;
// 1. 文件名长度
let name_len = filename.len() as u16;
stream.write_all(&name_len.to_be_bytes())?;
// 2. 文件名
stream.write_all(filename.as_bytes())?;
// 3. 文件大小
stream.write_all(&file_size.to_be_bytes())?;
// 4. 文件内容流式发送
let mut buffer = [0; 8192];
let mut sent: u64 = 0;
while sent < file_size {
let n = file.read(&mut buffer)?;
if n == 0 {
break;
}
stream.write_all(&buffer[..n])?;
sent += n as u64;
}
println!("✅ 发送完成,总计:{} bytes", sent);
Ok(())
}
6. 性能优化技巧
6.1 缓冲区管理
重用缓冲区而不是每次都创建新的
根据预期数据大小调整缓冲区大小
考虑使用BufReader和BufWriter进行缓冲I/O
use std::io::{BufReader, BufWriter};
fn handle_client(stream: TcpStream) -> std::io::Result<()> {
let mut reader = BufReader::new(&stream);
let mut writer = BufWriter::new(&stream);
let mut buffer = String::new();
reader.read_line(&mut buffer)?;
println!("Received: {}", buffer.trim());
writer.write_all(b"HTTP/1.1 200 OK\r\n\r\nHello from server!")?;
writer.flush()?;
Ok(())
}
更多推荐
所有评论(0)