Rust网络通信库nc的使用:高效异步TCP/UDP网络编程工具包
Rust网络通信库nc的使用:高效异步TCP/UDP网络编程工具包
简介
nc是一个Rust系统调用库,允许直接访问系统调用而无需依赖std
或libc
。它通过汇编直接访问系统调用,并支持最新的内核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(())
}
使用建议
- 错误处理:在实际应用中,应该为每个网络操作添加适当的错误处理
- 资源管理:使用Arc/Mutex等工具管理共享状态
- 性能优化:对于高并发场景,考虑使用连接池和对象池技术
- 日志记录:添加详细的日志记录以帮助调试
这些示例展示了nc库在TCP/UDP通信中的基本用法和高级功能,可以根据实际需求进行调整和扩展。