| name | rust-networking |
| description | | Use when this capability is needed. |
Rust Networking
Rust's networking ecosystem combines memory safety with zero-cost abstractions, making it ideal for protocol implementations, proxies, and networked services.
Core Crates
| Crate | Purpose | Async support |
|---|
std::net | TCP/UDP, DNS resolution | Blocking only |
tokio::net | TCP/UDP, Unix sockets, signal handling | Tokio |
async-std::net | TCP/UDP | async-std |
mio | Low-level I/O multiplexing (epoll/kqueue/IOCP) | Custom event loops |
socket2 | Socket creation, options, raw socket access | Platform-agnostic |
TCP Server
Blocking
use std::net::{TcpListener, TcpStream};
use std::io::{BufRead, BufReader, Write};
use std::thread;
fn handle_client(mut stream: TcpStream) {
let reader = BufReader::new(stream.try_clone().unwrap());
for line in reader.lines().flatten() {
let response = process_request(&line);
writeln!(stream, "{response}").unwrap();
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("0.0.0.0:8080")?;
for stream in listener.incoming() {
match stream {
Ok(stream) => { thread::spawn(|| handle_client(stream)); }
Err(e) => eprintln!("accept error: {e}"),
}
}
Ok(())
}
Async (Tokio)
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
async fn handle_client(mut stream: TcpStream) {
let (reader, mut writer) = stream.split();
let mut lines = BufReader::new(reader).lines();
while let Ok(Some(line)) = lines.next_line().await {
let response = process_request(&line).await;
if writer.write_all(response.as_bytes()).await.is_err() {
break;
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("0.0.0.0:8080").await?;
loop {
let (stream, _) = listener.accept().await?;
tokio::spawn((stream));
}
}
TCP Client
use tokio::io::{AsyncWriteExt, BufReader, AsyncBufReadExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
stream.write_all(b"Hello server\n").await?;
let mut reader = BufReader::new(&stream);
let mut response = String::new();
reader.read_line(&mut response).await?;
println!("Server says: {response}");
Ok(())
}
Connecting with Timeout
use tokio::time::{timeout, Duration};
match timeout(Duration::from_secs(5), TcpStream::connect("10.0.0.1:8080")).await {
Ok(Ok(stream)) => { }
Ok(Err(e)) => eprintln!("connection error: {e}"),
Err(_) => eprintln!("timeout connecting to 10.0.0.1:8080"),
}
UDP
use tokio::net::UdpSocket;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let socket = UdpSocket::bind("0.0.0.0:34254").await?;
socket.connect("127.0.0.1:8080").await?;
socket.send(b"hello").await?;
let mut buf = [0u8; 1024];
let n = socket.recv(&mut buf).await?;
println!("Received: {}", String::from_utf8_lossy(&buf[..n]));
Ok(())
}
UDP Broadcast Receiver
let socket = UdpSocket::bind("0.0.0.0:34254").await?;
socket.set_broadcast(true)?;
let mut buf = [0u8; 65535];
loop {
let (n, src) = socket.recv_from(&mut buf).await?;
println!("Received {} bytes from {src}", n);
}
Socket Options
use socket2::{Socket, Domain, Type, Protocol};
use std::net::{TcpListener, TcpStream};
fn configure_socket(addr: &str) -> std::io::Result<TcpListener> {
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?;
socket.set_reuse_address(true)?;
socket.set_reuse_port(true)?;
socket.set_keepalive(true)?;
socket.set_linger(Some(std::time::Duration::from_secs(10)))?;
socket.set_nodelay(true)?;
socket.set_send_buffer_size(64 * 1024)?;
socket.set_recv_buffer_size(64 * 1024)?;
socket.set_nonblocking(true)?;
let address: std::net::SocketAddr = addr.parse().unwrap();
socket.bind(&address.into())?;
socket.listen(1024)?;
: TcpListener = socket.();
(listener)
}
Binary Framing
When building custom protocols, frame messages to delimit boundaries.
use bytes::{BytesMut, BufMut, Bytes};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn read_frame(stream: &mut (impl AsyncReadExt + Unpin)) -> std::io::Result<Bytes> {
let mut len_buf = [0u8; 4];
stream.read_exact(&mut len_buf).await?;
let len = u32::from_be_bytes(len_buf) as usize;
let mut payload = vec![0u8; len];
stream.read_exact(&mut payload).await?;
Ok(Bytes::from(payload))
}
async fn write_frame(stream: &mut (impl AsyncWriteExt + Unpin), payload: &[u8]) -> std::io::Result<()> {
let len = payload.len() as u32;
stream.write_all(&len.()).?;
stream.(payload).?;
(())
}
Connection Pooling
use deadpool::managed::{self, RecycleResult, Manager};
use tokio::net::TcpStream;
struct TcpConnectionManager;
impl Manager for TcpConnectionManager {
type Type = TcpStream;
type Error = std::io::Error;
async fn create(&self) -> Result<TcpStream, Self::Error> {
TcpStream::connect("127.0.0.1:8080").await
}
async fn recycle(&self, conn: &mut TcpStream) -> RecycleResult<Self::Error> {
Ok(())
}
}
type Pool = managed::Pool<TcpConnectionManager>;
async fn use_pool(pool: &Pool) {
let mut conn = pool.get().await.unwrap();
}
Network Errors and Resilience
use std::io;
use tokio::time::{sleep, Duration};
async fn connect_with_retry(addr: &str, max_retries: u32) -> io::Result<TcpStream> {
let mut delay = Duration::from_millis(100);
for attempt in 0..max_retries {
match TcpStream::connect(addr).await {
Ok(stream) => return Ok(stream),
Err(e) if attempt == max_retries - 1 => return Err(e),
Err(_) => {
let jitter = rand::random::<f64>() * 0.5 + 0.75;
sleep(delay.mul_f64(jitter)).await;
delay *= 2;
if delay > Duration::from_secs(30) {
delay = Duration::from_secs(30);
}
}
}
}
()
}
Anti-Patterns
#[tokio::main]
async fn main() {
let listener = std::net::TcpListener::bind("0.0.0.0:8080").unwrap();
for stream in listener.incoming() { }
}
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
TcpStream::connect("slow-host:8080").await?;
tokio::time::timeout(Duration::from_secs(10), TcpStream::connect(addr)).await??;
let mut buf = Vec::new();
stream.read_to_end(&mut buf).await?;
let mut buf = vec![0u8; MAX_FRAME_SIZE];
let n = stream.read(&mut buf).await?;
Quick Navigation
Review Checklist
- Socket timeouts configured on all network I/O
- Read/write buffers are bounded, not unbounded
- TLS certificate validation is enabled (no
dangerous_accept_invalid_certs)
- Connection pools have max size and recycle policy
- Graceful shutdown drains in-flight connections
- Backpressure propagates through the protocol layer
References
Source: adxptived/Rust-Skills — distributed by TomeVault.