🎓 A1Learner
🔍

Rust TCP Options & Timeouts

TCP options are settings that change how a TcpStream behaves. Some small writes need to leave at once, and some reads need a deadline.

Send small writes at once

One call turns Nagle off. The getter reads the setting back:

client.set_nodelay(true).unwrap();
println!("nodelay = {}", client.nodelay().unwrap());

Cap the hops a packet may cross

TTL is a packet's travel budget. Each router hop spends one:

client.set_ttl(64).unwrap();
println!("ttl = {}", client.ttl().unwrap());

Give reads a deadline

The setter takes an Option. The getter shows what is set:

client.set_read_timeout(Some(Duration::from_millis(200))).unwrap();
println!("read timeout = {:?}", client.read_timeout().unwrap());

Connect with a deadline

Plain connect waits as long as the system allows. This form carries its own limit. It needs a SocketAddr, not text:

let stream = TcpStream::connect_timeout(&addr, Duration::from_secs(2));
println!("connected within the deadline = {}", stream.is_ok());

Example

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

fn main() {
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();
    let server = thread::spawn(move || {
        let (mut stream, _) = listener.accept().unwrap();
        let mut buf = [0u8; 4];
        stream.read_exact(&mut buf).unwrap();
        thread::sleep(Duration::from_millis(500));
        stream.write_all(b"slow reply").unwrap();
    });

    let mut client = TcpStream::connect(addr).unwrap();
    client.set_read_timeout(Some(Duration::from_millis(150))).unwrap();
    client.write_all(b"ping").unwrap();
    let mut buf = [0u8; 64];
    let first = client.read(&mut buf);
    println!("first read kind = {:?}", first.unwrap_err().kind());
    client.set_read_timeout(None).unwrap();
    let mut reply = String::new();
    client.read_to_string(&mut reply).unwrap();
    println!("second read = {reply}");
    server.join().unwrap();
}

Key points

  • Nagle's algorithm: TCP holds small writes for a moment to pack them into fewer packets.
  • It saves traffic.
  • It also adds delay
  • Use stream.set_nodelay(true) to switch Nagle off.
  • Small writes then leave at once.
  • Games and remote shells set this
  • Use set_read_timeout(Some(duration)) to give reads a deadline.
  • None switches the deadline back off
  • A read that runs out of time returns an Err.
  • The kind is WouldBlock on Linux and macOS.
  • It is TimedOut on Windows
  • Use set_ttl(n) to cap how many router hops a packet may cross.
  • ttl() reads the cap back
  • set_read_timeout(Some(Duration::ZERO)) fails with kind InvalidInput.

In this example, the server takes its time. It reads the request. It sleeps for half a second. Only then does it reply. The client gives its first read a 150 millisecond deadline. The deadline runs out first, so the read fails with kind WouldBlock. The client then clears the timeout and reads again. This time it waits, and the slow reply arrives whole. One warning: the kinds differ by system. Windows reports TimedOut for the same event.

Note

A zero timeout is not legal input.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

A chat program sends one tiny message per keystroke. Replies feel a beat late. Which knob fits?