在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,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(())
}
Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐