23、探索 Rust:从 USB 设备信息查询到网络编程基础

探索 Rust:从 USB 设备信息查询到网络编程基础

1. 连接 USB 设备信息查询项目

在 Rust 中,我们可以编写一个程序来查询连接的 USB 设备的详细信息。以下是具体步骤:

1.1 数据结构与实用函数编写
  • 创建新项目 :打开终端,执行以下命令创建一个新的 Rust 项目并进入项目目录:
cargo new usb && cd usb
  • 添加依赖 :在 Cargo.toml 文件中添加 libusb 依赖:
[dependencies]
libusb = "0.3.0"
  • 模块导入 :在 usb/src/main.rs 文件中添加以下模块导入:
use libusb::{Context, Device, DeviceHandle};
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::fmt;
  • 定义数据结构
#[derive(Debug)]
struct USBError {
    err: String,
}

struct USBList {
    list: Vec<USBDetails>,
}

#[derive(Debug)]
struct USBDetails {
    manufacturer: String,
    product: String,
    serial_number: String,
    bus_number: u8,
    device_address: u8,
    vendor_id: u16,
    product_id: u16,
    maj_device_version: u8,
    min_device_version: u8,
}
  • 实现 Display 特征 :为 USBList 结构体实现 Display 特征,用于自定义格式化输出:
impl fmt::Display for USBList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(for usb in &self.list {
            writeln!(f, "\nUSB Device details")?;
            writeln!(f, "Manufacturer: {}", usb.manufacturer)?;
            writeln!(f, "Product: {}", usb.product)?;
            writeln!(f, "Serial number: {}", usb.serial_number)?;
            writeln!(f, "Bus number: {}", usb.bus_number)?;
            writeln!(f, "Device address: {}", usb.device_address)?;
            writeln!(f, "Vendor Id: {}", usb.vendor_id)?;
            writeln!(f, "Product Id: {}", usb.product_id)?;
            writeln!(f, "Major device version: {}", usb.maj_device_version)?;
            writeln!(f, "Minor device version: {}", usb.min_device_version)?;
        })
    }
}
  • 实现 From 特征 :为 USBError 结构体实现 From 特征,用于自动转换错误类型:
impl From<libusb::Error> for USBError {
    fn from(_e: libusb::Error) -> Self {
        USBError {
            err: "Error in accessing USB device".to_string(),
        }
    }
}

impl From<std::io::Error> for USBError {
    fn from(e: std::io::Error) -> Self {
        USBError { err: e.to_string() }
    }
}
  • 写入文件函数 :编写一个函数将设备信息写入文件:
//Function to write details to output file
fn write_to_file(usb: USBList) -> Result<(), USBError> {
    let mut file_handle = File::create("usb_details.txt")?;
    write!(file_handle, "{}\n", usb)?;
    Ok(())
}
1.2 编写主函数
fn main() -> Result<(), USBError> {
    // Get libusb context
    let context = Context::new()?;

    //Get list of devices
    let mut device_list = USBList { list: vec![] };
    for device in context.devices()?.iter() {
        let device_desc = device.device_descriptor()?;
        let device_handle = context
           .open_device_with_vid_pid(
                device_desc.vendor_id(), 
                device_desc.product_id())
           .unwrap();

        // For each USB device, get the information
        let usb_details = get_device_information(
            device, &device_handle)?;
        device_list.list.push(usb_details);
    }
    println!("\n{}", device_list);
    write_to_file(device_list)?;
    Ok(())
}
1.3 获取设备详细信息函数
// Function to print device information
fn get_device_information(device: Device, handle: 
    &DeviceHandle) -> Result<USBDetails, USBError> {
    let device_descriptor = 
        device.device_descriptor()?;
    let timeout = Duration::from_secs(1);
    let languages = handle.read_languages(timeout)?;
    let language = languages[0];
    // Get device manufacturer name
    let manufacturer = 
        handle.read_manufacturer_string(
            language, &device_descriptor, timeout)?;
    // Get device USB product name
    let product = handle.read_product_string(
        language, &device_descriptor, timeout)?;
    //Get product serial number
    let product_serial_number =
        match handle.read_serial_number_string(
            language, &device_descriptor, timeout) {
            Ok(s) => s,
            Err(_) => "Not available".into(),
        };
    // Populate the USBDetails struct
    Ok(USBDetails {
        manufacturer,
        product,
        serial_number: product_serial_number,
        bus_number: device.bus_number(),
        device_address: device.address(),
        vendor_id: device_descriptor.vendor_id(),
        product_id: device_descriptor.product_id(),
        maj_device_version: 
            device_descriptor.device_version().0,
        min_device_version: 
            device_descriptor.device_version().1,
    })
}

在运行此程序前,确保已将 USB 设备(如 U 盘)插入计算机,然后在终端执行 cargo run 命令。程序将在终端打印出连接的 USB 设备的详细信息,并将信息写入 usb_details.txt 文件。

2. 网络编程基础

在现代操作系统中,TCP/IP 协议是网络通信的基础。接下来,我们将学习 Rust 中网络编程的基础知识。

2.1 网络基础回顾

互联网通过各种网络协议和标准,使不同网络中的主机能够相互通信。TCP/IP 协议栈分为四层:
| 层次 | 描述 |
| ---- | ---- |
| 数据链路层 | 包含设备驱动和网络接口卡,负责将 IP 层的数据组装成数据帧并传输 |
| IP 层 | 负责将数据组装成数据包并进行路由,是无连接协议 |
| 传输层 | 有 TCP 和 UDP 两种协议,TCP 提供可靠的连接,UDP 无连接但速度快 |
| 应用层 | 处理具体的应用协议,如 HTTP |

2.2 Rust 标准库中的网络原语

Rust 标准库中的 std::net 模块提供了处理网络通信的基础数据结构:
- Ipv4Addr :存储 32 位的 IPv4 地址。
- Ipv6Addr :存储 128 位的 IPv6 地址。
- SocketAddrV4 :表示 IPv4 套接字地址。
- SocketAddrV6 :表示 IPv6 套接字地址。
- IpAddr :枚举类型,可存储 IPv4 或 IPv6 地址。
- SocketAddr :枚举类型,可存储 IPv4 或 IPv6 套接字地址。

以下是使用这些数据结构的示例代码:

use std::net::{Ipv4Addr, Ipv6Addr};

fn main() {
    // Create a new IPv4 address with four 8-bit integers
    let ip_v4_addr1 = Ipv4Addr::new(106, 201, 34, 209);
    // Use the built-in constant to create a new loopback 
    // (localhost) address
    let ip_v4_addr2 = Ipv4Addr::LOCALHOST;
    println!(
        "Is ip_v4_addr1 a loopback address? {}",
        ip_v4_addr1.is_loopback()
    );
    println!(
        "Is ip_v4_addr2 a loopback address? {}",
        ip_v4_addr2.is_loopback()
    );
    //Create a new IPv6 address with eight 16-bit  
    // integers, represented in hex
    let ip_v6_addr = Ipv6Addr::new(2001, 0000, 3238, 
        0xDFE1, 0063, 0000, 0000, 0xFEFB);
    println!("IPV6 segments {:?}", ip_v6_addr.segments());
}
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

fn main() {
    // Create an ipv4 address
    let ip_v4_addr = IpAddr::V4(Ipv4Addr::new(106, 201, 34, 
        209));
    // check if an address is ipv4 or ipv6 address
    println!("Is ip_v4_addr an ipv4 address? {}", 
        ip_v4_addr.is_ipv4());
    println!("Is ip_v4_addr an ipv6 address? {}", 
        ip_v4_addr.is_ipv6());

    // Create an ipv6 address
    let ip_v6_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 
        0, 0, 0, 1));
    println!("Is ip_v6_addr an ipv6 address? {}", 
        ip_v6_addr.is_ipv6());
}
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
fn main() {
    // Create an ipv4 socket 
    let socket = SocketAddr::new(IpAddr::V4(
        Ipv4Addr::new(127,0,0,1)),8000);
    println!("Socket address is {}, port is {}", socket.ip(), socket.port());
}

通过这些示例,我们可以看到如何使用 Rust 标准库中的网络原语来创建和操作网络地址和套接字。在后续的学习中,我们将深入探讨如何使用 TCP 和 UDP 进行网络编程,并实现一个 TCP 反向代理项目。

探索 Rust:从 USB 设备信息查询到网络编程基础

2.3 使用 TCP 和 UDP 进行网络编程

在 Rust 中,我们可以利用 std::net 模块使用 TCP 和 UDP 进行网络编程。

2.3.1 TCP 编程

TCP 是一种面向连接的、可靠的传输协议。以下是一个简单的 TCP 服务器和客户端示例:

TCP 服务器示例

use std::net::TcpListener;
use std::io::{Read, Write};

fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    for stream in listener.incoming() {
        let mut stream = stream?;
        let mut buffer = [0; 1024];
        let bytes_read = stream.read(&mut buffer)?;
        if bytes_read > 0 {
            let request = String::from_utf8_lossy(&buffer[0..bytes_read]);
            println!("Received request: {}", request);
            stream.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello, World!")?;
        }
    }
    Ok(())
}

此代码创建了一个监听本地 8080 端口的 TCP 服务器。当有客户端连接时,它会读取客户端发送的数据,并返回一个简单的 HTTP 响应。

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")?;
    stream.write_all(b"GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\n\r\n")?;
    let mut buffer = [0; 1024];
    let bytes_read = stream.read(&mut buffer)?;
    if bytes_read > 0 {
        let response = String::from_utf8_lossy(&buffer[0..bytes_read]);
        println!("Received response: {}", response);
    }
    Ok(())
}

这个客户端代码连接到本地 8080 端口的 TCP 服务器,发送一个简单的 HTTP 请求,并打印服务器的响应。

2.3.2 UDP 编程

UDP 是一种无连接的、不可靠的传输协议,但它速度快、开销小。以下是一个简单的 UDP 服务器和客户端示例:

UDP 服务器示例

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    let socket = UdpSocket::bind("127.0.0.1:8081")?;
    let mut buf = [0; 1024];
    loop {
        let (num_bytes, src_addr) = socket.recv_from(&mut buf)?;
        let data = &buf[0..num_bytes];
        println!("Received {} bytes from {}", num_bytes, src_addr);
        socket.send_to(data, src_addr)?;
    }
}

此代码创建了一个监听本地 8081 端口的 UDP 服务器。它接收客户端发送的数据,并将数据原样返回给客户端。

UDP 客户端示例

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    let socket = UdpSocket::bind("127.0.0.1:0")?;
    let server_addr = "127.0.0.1:8081";
    let msg = b"Hello, UDP Server!";
    socket.send_to(msg, server_addr)?;
    let mut buf = [0; 1024];
    let (num_bytes, _) = socket.recv_from(&mut buf)?;
    let response = &buf[0..num_bytes];
    println!("Received response: {}", String::from_utf8_lossy(response));
    Ok(())
}

这个客户端代码向本地 8081 端口的 UDP 服务器发送一条消息,并打印服务器的响应。

2.4 编写 TCP 反向代理项目

反向代理是一种服务器,它接收客户端的请求,并将请求转发到后端服务器,然后将后端服务器的响应返回给客户端。以下是一个简单的 TCP 反向代理示例:

use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};

fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8082")?;
    for stream in listener.incoming() {
        let mut client_stream = stream?;
        let mut backend_stream = TcpStream::connect("127.0.0.1:8080")?;

        let mut buffer = [0; 1024];
        let bytes_read = client_stream.read(&mut buffer)?;
        if bytes_read > 0 {
            backend_stream.write_all(&buffer[0..bytes_read])?;
            let mut backend_buffer = [0; 1024];
            let backend_bytes_read = backend_stream.read(&mut backend_buffer)?;
            if backend_bytes_read > 0 {
                client_stream.write_all(&backend_buffer[0..backend_bytes_read])?;
            }
        }
    }
    Ok(())
}

此代码创建了一个监听本地 8082 端口的 TCP 反向代理。当有客户端连接时,它将客户端的请求转发到本地 8080 端口的后端服务器,并将后端服务器的响应返回给客户端。

总结

通过以上内容,我们学习了如何在 Rust 中查询连接的 USB 设备信息,回顾了网络编程的基础知识,包括 TCP/IP 协议栈的各层功能,掌握了 Rust 标准库中网络原语的使用,学会了使用 TCP 和 UDP 进行网络编程,并且实现了一个简单的 TCP 反向代理项目。Rust 的标准库为我们提供了强大而简洁的工具,使得我们可以方便地进行设备 I/O 和网络编程。希望这些知识能帮助你在 Rust 编程的道路上更进一步。

以下是 TCP/IP 协议栈层次关系的 mermaid 流程图:

graph LR
    A[应用层] --> B[传输层]
    B --> C[IP 层]
    C --> D[数据链路层]

这个流程图清晰地展示了 TCP/IP 协议栈各层之间的层次关系,方便我们理解数据在网络中的传输过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值