计算机系统应用教程网站

网站首页 > 技术文章 正文

Rust Socket编程之基础网络操作

btikc 2025-01-10 11:50:53 技术文章 20 ℃ 0 评论

前言

在这一部分,我们将详细讲解如何使用Rust进行基础的网络操作。我们将通过示例代码来展示如何实现最简单的TCP和UDP通信,并逐步深入到Rust标准库std::net模块的使用。

创建和绑定Socket

在Rust中,我们使用std::net模块来创建和管理Socket。该模块支持两种主要的传输协议:TCPUDP。我们将分别介绍如何使用TCP和UDP进行网络操作。

1. TCP Socket:创建和绑定

TCP是一种面向连接的协议,客户端和服务器之间必须先建立连接,才能进行数据传输。我们可以通过TcpListener来实现服务器端的监听,使用TcpStream来进行客户端的连接。

步骤:

? 服务器端使用TcpListener来监听端口。

? 客户端使用TcpStream来连接到服务器。

? 服务器和客户端通过read和write方法进行数据传输。

我们先cargo new创建一个工作空间,并分别创建两个名为tcp_server和tcp_client的包,操作如下所示:

cargo new rust_socket_demo
cd rust_socket_demo/
cargo new tcp_server
cargo new tcp_client

修改根路径下Cargo.toml配置文件,具体如下所示:

[package]
name = "rust_socket_demo"
version = "0.1.0"
edition = "2021"

[workspace]
members = ["tcp_server", "tcp_client"]

[dependencies]

2. 编写tcp_server服务端的代码

修改tcp_server/main.rs文件,键入如下代码:

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

fn handle_client(mut stream: TcpStream) {
    let mut buffer = [0; 512];
    match stream.read(&mut buffer) {
        Ok(size) => {
            println!("Request: {}", String::from_utf8_lossy(&buffer[..size]));
            stream.write(b"hello from server").unwrap();
        }
        Err(e) => {
            println!("Failed to read from stream: {}", e);
        }
    }
}

fn main() {
    // 创建一个TCP监听器,绑定到本地地址127.0.0.1:9527
    let listener = TcpListener::bind("127.0.0.1:9527").unwrap();
    println!("Listening on: {}", listener.local_addr().unwrap());

    // 等待客户端连接
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                std::thread::spawn(move || {
                    handle_client(stream);
                });
            }
            Err(e) => {
                println!("Failed to connect: {}", e);
            }
        }
    }
}

3. 编写tcp_client客户端的代码

修改tcp_client/main.rs文件,键入如下代码:

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

fn main() {
    // 客户端连接到127.0.0.1:9527
    let mut stream = TcpStream::connect("127.0.0.1:9527").unwrap();
    // 发送数据给服务器端
    stream.write("hello".as_bytes()).unwrap();
    // 接收服务器端返回的数据
    let mut buffer = [0; 512];
    let size = stream.read(&mut buffer).unwrap();
    println!("Received from server: {}", String::from_utf8_lossy(&buffer[..size]));
}

运行结果如下所示:

// 服务端
cargo run -p tcp_server
   Compiling tcp_server v0.1.0 (/Users/Alen/Workspaces/Rust/rust_socket_demo/tcp_server)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.62s
     Running `target/debug/tcp_server`
Listening on: 127.0.0.1:9527
Request: hello


// 客户端
 cargo run -p tcp_client
   Compiling tcp_client v0.1.0 (/Users/Alen/Workspaces/Rust/rust_socket_demo/tcp_client)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/tcp_client`
Received from server: hello from server

步骤解析:

  • TcpListener::bind:用于在服务器端创建一个监听器,并绑定到指定的IP地址和端口(在这里是127.0.0.1:9527)。
  • TcpStream::connect:客户端通过此方法连接到指定的服务器地址。
  • read:服务器和客户端使用read方法从流中读取数据。
  • write:将数据写入流中,通过TCP发送给对方。

??注意:Read 和 Write 是用于 TcpStream 类型的 trait(特性),它们提供了读取和写入流的功能。虽然 TcpStream 本身并不直接导入 Read 和 Write,但你还是需要导入这些 trait 来能够调用 stream.read() 和 stream.write() 方法。Rust 的 trait 是一种约定,它允许类型通过实现特定的 trait 来支持特定的操作。TcpStream 实现了Read 和 Write trait,因此你需要导入这两个 trait,才能在 TcpStream 对象上调用 read 和write方法。如果不导入这些 trait,你将会遇到编译错误,提示 TcpStream 没有实现 read 和 write。

4. UDP Socket:创建和绑定

与TCP不同,UDP是无连接的,它不会建立连接,而是直接发送数据包。UDP的优势是低延迟,但它不保证数据的顺序或完整性。

步骤:

? 使用UdpSocket进行UDP通信。

? 通过send_to和recv_from方法发送和接收数据。

在原有的代码目录使用cargo分别创建名为udp_server和udp_client的包,具体如下所示:

cargo new udp_server
cargo new udp_client

??注意: 需要将udp_server和udp_client这两个包加入到Cargo.toml配置文件中

5. 编写udp_server服务端的代码

修改udp_server/main.rs文件,键入如下代码:

use std::net::UdpSocket;

fn main() {
    // 创建一个udp socket,并绑定到本地地址0.0.0.0:9527
    let socket = UdpSocket::bind("0.0.0.0:9527").expect("Could not bind socket");

    let mut buf = [0; 512];
    loop {
        // 接收数据
        let (size, src) = socket.recv_from(&mut buf).expect("Didn't receive data");
        println!("Received {} bytes from {:?}", src, String::from_utf8_lossy(&buf[..size]));

        // 向客户端发送响应
        socket.send_to(b"hello from udp server", &src).expect("Could not send data");
    }
}

6. 编写udp_client客户端的代码

修改udp_client/main.rs文件,键入如下代码:

use std::net::UdpSocket;

fn main() {
    // 创建udp socket
    let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address");

    // 发送数据到服务器
    socket.send_to(b"hello from udp client", "0.0.0.0:9527").expect("couldn't send message");
    let mut buf = [0; 512];
    // 接收服务器响应
    let (size, _) = socket.recv_from(&mut buf).expect("Didn't receive data");
    println!("Received from server: {}", String::from_utf8_lossy(&buf[..size]));
}

运行结果如下所示:

// 服务端
?  rust_socket_demo git:(master) ? cargo run -p udp_server
   Compiling udp_server v0.1.0 (/Users/Alen/Workspaces/Rust/rust_socket_demo/udp_server)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.64s
     Running `target/debug/udp_server`
Received 127.0.0.1:58359 bytes from "hello from udp client"


// 客户端
?  rust_socket_demo git:(master) ? cargo run -p udp_client
   Compiling udp_client v0.1.0 (/Users/Alen/Workspaces/Rust/rust_socket_demo/udp_client)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/udp_client`
Received from server: hello from udp server

步骤解析:

  • UdpSocket::bind:在UDP通信中,客户端和服务器都需要绑定到一个本地地址和端口。这里服务器绑定到127.0.0.1:8080,客户端使用动态端口。
  • recv_from:接收来自客户端的数据包。与TCP不同,UDP是无连接的,因此数据包可能来自多个客户端。
  • send_to:发送数据到指定的地址。

发送和接收数据

发送和接收数据是Socket编程的核心操作。在Rust中,TCP和UDP都使用类似的API进行数据的读取和写入,具体区别在于TCP是基于流的,而UDP是基于数据报的。

  • TCP的发送与接收:通过TcpStream的read和write方法实现,read会阻塞当前线程直到数据被接收。
  • UDP的发送与接收:通过UdpSocket的send_to和recv_from方法实现,recv_from会从指定的地址接收数据,而send_to则发送数据到指定地址。

Rust网络API详解

Rust标准库的std::net模块提供了几种常见的网络API:

1.TcpListener

  • bind(&self, addr: &SocketAddr): 将监听器绑定到指定的IP地址和端口。
  • incoming(&self): 返回一个迭代器,遍历所有连接的客户端。
  • accept(&self): 接受一个客户端的连接,返回一个TcpStream。

2.TcpStream

  • connect(addr: &SocketAddr): 连接到指定的服务器。
  • read(&mut self, buf: &mut [u8]): 从流中读取数据。
  • write_all(&mut self, buf: &[u8]): 向流中写入数据

3.UdpSocket

  • bind(&self, addr: &SocketAddr): 绑定UDP套接字到本地地址。
  • send_to(&self, buf: &[u8], addr: &SocketAddr): 发送数据到指定的地址。
  • recv_from(&self, buf: &mut [u8]): 接收数据报,返回数据的大小和来源地址。

小结

  • TCP和UDP是最常见的两种网络协议,它们分别适用于不同的场景:TCP保证数据的可靠性和顺序,适合需要保证数据完整性的应用;UDP则更快,但不保证数据的可靠性,适合实时通信场景。
  • 在Rust中,std::net模块提供了丰富的API来进行网络通信。TcpListener和TcpStream用于TCP通信,而UdpSocket用于UDP通信。
  • 基本的网络操作包括:创建Socket、绑定端口、发送和接收数据、关闭连接等。理解这些基本操作是构建更复杂网络应用的基础。

这一部分讲解了如何在Rust中使用TCP和UDP进行基本的网络通信操作,为你进一步学习异步编程和更复杂的网络应用打下了基础。如果本文对你有所帮助还请关注转发,后续继续分享跟多关于rust相关知识!!!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表