Rust网络通信库nc的使用:高效异步TCP/UDP网络编程工具包

Rust网络通信库nc的使用:高效异步TCP/UDP网络编程工具包

简介

nc是一个Rust系统调用库,允许直接访问系统调用而无需依赖stdlibc。它通过汇编直接访问系统调用,并支持最新的内核API,如Linux 5.0+引入的io-uring和pidfd。

主要特性:

  • 不需要glibc或musl
  • 通过汇编直接访问系统调用
  • 没有全局errno变量,每个函数返回errno
  • 支持最新内核API

安装

在Cargo.toml中添加:

[dependencies]
nc = "0.9"

示例

以下是几个使用nc进行网络通信的示例:

1. TCP客户端示例

use nc::{socket, connect, send, recv, close};
use std::ffi::CString;

fn main() {
    // 创建TCP socket
    let sockfd = unsafe { socket(nc::AF_INET, nc::SOCK_STREAM, 0) }.unwrap();
    
    // 连接服务器
    let serv_addr = nc::sockaddr_in {
        sin_family: nc::AF_INET as u16,
        sin_port: 8080.to_be(),
        sin_addr: nc::in_addr {
            s_addr: nc::htonl(nc::INADDR_LOOPBACK),
        },
        ..Default::default()
    };
    
    let ret = unsafe { connect(sockfd, &serv_addr as *const _ as *const _, std::mem::size_of_val(&serv_addr)) };
    if ret.is_err() {
        eprintln!("Connect failed: {}", nc::strerror(ret.err().unwrap()));
        return;
    }
    
    // 发送数据
    let msg = CString::new("Hello from nc!").unwrap();
    let ret = unsafe { send(sockfd, msg.as_ptr() as *const _, msg.as_bytes().len(), 0) };
    if ret.is_err() {
        eprintln!("Send failed: {}", nc::strerror(ret.err().unwrap()));
    } else {
        println!("Sent {} bytes", ret.unwrap());
    }
    
    // 接收数据
    let mut buf = [0u8; 1024];
    let ret = unsafe { recv(sockfd, buf.as_mut_ptr() as *mut _, buf.len(), 0) };
    if ret.is_err() {
        eprintln!("Recv failed: {}", nc::strerror(ret.err().unwrap()));
    } else {
        let n = ret.unwrap();
        println!("Received {} bytes: {:?}", n, &buf[..n as usize]);
    }
    
    // 关闭socket
    unsafe { close(sockfd) };
}

2. UDP服务器示例

use nc::{socket, bind, recvfrom, sendto, close};
use std::ffi::CString;

fn main() {
    // 创建UDP socket
   极客时间
    let sockfd = unsafe { socket(nc::AF_INET, nc::SOCK_DGRAM, 0) }.unwrap();
    
    // 绑定端口
    let serv_addr = nc::sockaddr_in {
        sin_family: nc::AF_INET as u16,
        sin_port: 8080.to_be(),
        sin_addr: nc::in_addr {
            s_addr: nc::htonl(nc::INADDR_ANY),
        },
        ..Default::default()
    };
    
    let ret = unsafe { bind(sockfd, &serv_addr as *const _ as *const _, std::mem::size_of_val(&serv_addr)) };
    if ret is_err() {
        eprintln!("Bind failed: {}", nc::strerror(ret.err().unwrap()));
        return;
    }
    
    println!("UDP server listening on port 8080");
    
    loop {
        // 接收数据
        let mut buf = [0u8; 1024];
        let mut client_addr: nc::sockaddr_in = Default::default();
        let mut addr_len = std::mem::size_of_val(&client_addr) as nc::socklen_t;
        
        let ret = unsafe {
            recvfrom(
                sockfd,
                buf.as_mut_ptr() as *mut _,
                buf.len(),
                0,
                &mut client_addr as *mut _ as *mut _,
                &mut addr_len,
            )
        };
        
        if ret.is_err() {
            eprintln!("Recvfrom failed: {}", nc::strerror(ret.err().unwrap()));
            continue;
        }
        
        let n = ret.unwrap();
        println!("Received {} bytes from {:?}", n, client_addr);
        
        // 发送响应
        let msg = CString::new("Hello UDP client!").unwrap();
        let ret = unsafe {
            sendto(
                sockfd,
                msg.as_ptr() as *const _,
                msg.as_bytes().len(),
                0,
                &client_addr as *const _ as *const _,
                addr_len,
            )
        };
        
        if ret.is_err() {
            eprintln!("Sendto failed: {}", nc::strerror(ret.err().unwrap()));
        }
    }
    
    // 关闭socket
    unsafe { close(sockfd) };
}

3. 异步I/O示例 (使用io_uring)

use nc::{io_uring_setup, io_uring_enter, io_uring_register, io_uring_params};

fn main() {
    // 设置io_uring
    let mut params = io极客时间_uring_params::default();
    let ring_fd = unsafe { io_uring_setup(32, &mut params) }.unwrap();
    
    // 注册文件描述符
    let fds = [ring_fd];
    let ret = unsafe { io_uring_register(ring_fd, nc::IORING_REGISTER_FILES, fds.as_ptr() as *const _, fds.len()) };
    if ret.is_err() {
        eprintln!("Register failed: {}", nc::strerror(ret.err().unwrap()));
    }
    
    // 提交和完成队列操作
    // ... (实际应用中需要更复杂的设置)
    
    println!("io_uring setup completed with fd: {}", ring_fd);
}

支持的操作系统和架构

nc支持多种操作系统和架构:

  • linux
    • aarch64, arm, loongarch64, mips, mips64, mips64el, mipsel
    • powerpc64, powerpc64el, riscv64, s390x, x86, x86-64
  • android
    • aarch64
  • freebsd
    • x86-64
  • netbsd
    • x86-64
  • mac os
    • x86-64

许可证

本库使用Apache-2.0许可证。


1 回复

Rust网络通信库nc的使用:高效异步TCP/UDP网络编程工具包

以下是基于nc库的完整TCP/UDP通信示例,包含客户端和服务器的完整实现:

完整TCP通信示例

TCP服务器端

use nc::tcp::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 绑定到本地8080端口
    let listener = TcpListener::bind("127.0.0.1:8080").await?;
    println!("TCP server started on 127.0.0.1:8080");
    
    loop {
        // 接受新连接
        let (mut stream, addr) = listener.accept().await?;
        println!("New connection from: {}", addr);
        
        // 为每个连接生成异步任务
        tokio::spawn(async move {
            let mut buf = [0; 1024];
            
            // 读取客户端数据
            match stream.read(&mut buf).await {
                Ok(n) if n == 0 => {
                    println!("Connection closed by client: {}", addr);
                    return;
                }
                Ok(n) => {
                    let received = String::from_utf8_lossy(&buf[..n]);
                    println!("Received from {}: {}", addr, received);
                    
                    // 发送响应
                    let response = format!("Server received {} bytes", n);
                    if let Err(e) = stream.write_all(response.as_bytes()).await {
                        eprintln!("Error writing to socket: {}", e);
                    }
                }
                Err(e) => {
                    eprintln!("Error reading from socket {}: {}", addr, e);
                }
            }
        });
    }
}

TCP客户端端

use nc::tcp::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 带超时连接到服务器
    let connect_result = tokio::time::timeout(
        Duration::from_secs(5),
        TcpStream::connect("127.0.0.1:8080")
    ).await;
    
    let mut stream = match connect_result {
        Ok(Ok(s)) => {
            println!("Connected to server!");
            s
        }
        Ok(Err(e)) => return Err(e.into()),
        Err(_) => return Err("Connection timeout".into()),
    };
    
    // 发送消息
    let message = "Hello from TCP client!";
    stream.write_all(message.as_bytes()).await?;
    println!("Sent: {}", message);
    
    // 接收响应
    let mut buf = [0; 1024];
    let n = stream.read(&mut buf).await?;
    println!("Server response: {}", String::from_utf8_lossy(&buf[..n]));
    
    Ok(())
}

完整UDP通信示例

UDP服务器端

use nc::udp::UdpSocket;
use tokio::io::AsyncReadExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 绑定到本地8080端口
    let socket = UdpSocket::bind("127.0.0.1:8080").await?;
    println!("UDP server started on {}", socket.local_addr()?);
    
    let mut buf = [0; 1024];
    
    loop {
        // 接收数据
        let (n, addr) = socket.recv_from(&mut buf).await?;
        let received = String::from_utf8_lossy(&buf[..n]);
        println!("Received from {}: {}", addr, received);
        
        // 发送响应
        let response = format!("UDP server received {} bytes", n);
        socket.send_to(response.as_bytes(), addr).await?;
    }
}

UDP客户端端

use nc::udp::UdpSocket;
use tokio::io::AsyncReadExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建并绑定UDP socket
    let socket = UdpSocket::bind("127.0.0.1:0").await?;
    println!("Client socket bound to {}", socket.local_addr()?);
    
    // 发送数据到服务器
    let message = "Hello from UDP client!";
    socket.send_to(message.as_bytes(), "127.0.0.1:8080").await?;
    println!("Sent: {}", message);
    
    // 接收响应
    let mut buf = [0; 1024];
    let (n, addr) = socket.recv_from(&mut buf).await?;
    println!("Received from {}: {}", addr, String::from_utf8_lossy(&buf[..n]));
    
    Ok(())
}

高级功能示例:带心跳的TCP长连接

use nc::tcp::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::time::{self, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
    
    // 心跳任务
    let heartbeat = tokio::spawn(async move {
        let mut interval = time::interval(Duration::from_secs(5));
        loop {
            interval.tick().await;
            if let Err(e) = stream.write_all(b"HEARTBEAT").await {
                eprintln!("Heartbeat failed: {}", e);
                break;
            }
        }
    });
    
    // 数据接收任务
    let mut buf = [0; 1024];
    loop {
        match stream.read(&mut buf).await {
            Ok(0) => break,  // 连接关闭
            Ok(n) => {
                println!("Received: {}", String::from_utf8_lossy(&buf[..n]));
            }
            Err(e) => {
                eprintln!("Read error: {}", e);
                break;
            }
        }
    }
    
    heartbeat.abort();
    Ok(())
}

使用建议

  1. 错误处理:在实际应用中,应该为每个网络操作添加适当的错误处理
  2. 资源管理:使用Arc/Mutex等工具管理共享状态
  3. 性能优化:对于高并发场景,考虑使用连接池和对象池技术
  4. 日志记录:添加详细的日志记录以帮助调试

这些示例展示了nc库在TCP/UDP通信中的基本用法和高级功能,可以根据实际需求进行调整和扩展。

回到顶部