探索 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 协议栈各层之间的层次关系,方便我们理解数据在网络中的传输过程。
超级会员免费看
47

被折叠的 条评论
为什么被折叠?



