Network Operations
Overview
The std::net module provides TCP and UDP networking primitives. Create servers and clients for network communication with a simple, synchronous API.
Code Example
#![allow(unused)]
fn main() {
use std::net::TcpListener;
// Simple TCP listener demonstration
let listener_result = TcpListener::bind("127.0.0.1:0");
match listener_result {
Ok(listener) => {
let local_addr = listener.local_addr().unwrap();
println!("TCP listener bound to: {}", local_addr);
}
Err(e) => println!("TCP bind failed: {}", e),
}
}
Explanation
TcpListener::bind(): Binds to IP and port for incoming connections- Port
0: OS assigns random available port local_addr(): Gets the actual address bound to- Returns
Resultfor error handling
Key Concepts
TCP Server
#![allow(unused)]
fn main() {
use std::net::TcpListener;
use std::io::{Read, Write};
let listener = TcpListener::bind("127.0.0.1:8080")?;
for stream in listener.incoming() {
match stream {
Ok(mut stream) => {
let mut buffer = [0; 512];
stream.read(&mut buffer)?;
stream.write(b"HTTP/1.1 200 OK\r\n\r\n")?;
}
Err(e) => eprintln!("Connection failed: {}", e),
}
}
}
TCP Client
#![allow(unused)]
fn main() {
use std::net::TcpStream;
use std::io::{Read, Write};
let mut stream = TcpStream::connect("example.com:80")?;
stream.write(b"GET / HTTP/1.0\r\n\r\n")?;
let mut response = String::new();
stream.read_to_string(&mut response)?;
println!("Response: {}", response);
}
UDP Sockets
#![allow(unused)]
fn main() {
use std::net::UdpSocket;
let socket = UdpSocket::bind("127.0.0.1:0")?;
socket.send_to(b"Hello", "127.0.0.1:8080")?;
let mut buf = [0; 512];
let (amt, src) = socket.recv_from(&mut buf)?;
println!("Received {} bytes from {}", amt, src);
}
Use Cases
- Web Servers: HTTP/HTTPS servers
- APIs: REST or RPC services
- Chat Applications: Real-time messaging
- Game Servers: Multiplayer games
- IoT: Device communication
- Microservices: Service-to-service communication
Best Practices
✅ Do:
- Use
0.0.0.0for public servers,127.0.0.1for local only - Set timeouts with
set_read_timeout(),set_write_timeout() - Use thread pools or async for handling multiple connections
- Handle partial reads/writes in loops
❌ Don’t:
- Block indefinitely without timeouts
- Forget to handle connection errors
- Create thread per connection (use thread pools)
- Assume all data arrives in one read
Advanced Patterns
Threaded Server
#![allow(unused)]
fn main() {
use std::net::TcpListener;
use std::thread;
let listener = TcpListener::bind("127.0.0.1:8080")?;
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(|| {
handle_client(stream);
});
}
Err(e) => eprintln!("Error: {}", e),
}
}
}
Connection Timeouts
#![allow(unused)]
fn main() {
use std::time::Duration;
use std::net::TcpStream;
let mut stream = TcpStream::connect("example.com:80")?;
stream.set_read_timeout(Some(Duration::from_secs(5)))?;
stream.set_write_timeout(Some(Duration::from_secs(5)))?;
}
Non-Blocking I/O
#![allow(unused)]
fn main() {
use std::net::TcpStream;
let stream = TcpStream::connect("127.0.0.1:8080")?;
stream.set_nonblocking(true)?;
// Now read/write operations return immediately
// with WouldBlock error if not ready
}
Protocol Implementation Example
Simple Echo Server
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;
fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
let mut buffer = [0; 512];
loop {
let n = stream.read(&mut buffer)?;
if n == 0 {
return Ok(()); // Connection closed
}
stream.write_all(&buffer[..n])?;
}
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Echo server listening on port 8080");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(|| {
if let Err(e) = handle_client(stream) {
eprintln!("Error handling client: {}", e);
}
});
}
Err(e) => eprintln!("Connection error: {}", e),
}
}
Ok(())
}
IPv4 vs IPv6
#![allow(unused)]
fn main() {
use std::net::{TcpListener, SocketAddr};
// IPv4
let v4 = TcpListener::bind("127.0.0.1:8080")?;
// IPv6
let v6 = TcpListener::bind("[::1]:8080")?;
// Both (dual-stack)
let addr: SocketAddr = "[::]:8080".parse()?;
let dual = TcpListener::bind(addr)?;
}
Async Alternatives
For production use, consider async runtimes:
- tokio: Most popular async runtime
- async-std: Async version of std
- smol: Lightweight async runtime
// Example with tokio (not std)
// #[tokio::main]
// async fn main() {
// let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await?;
// // ...
// }
Related Features
- Threading - Multi-threaded servers
- Error Handling - Handling network errors