🎓 A1Learner
🔍
0 of 149 lessons completed

Rust TCP Basics

TCP is a connected stream of bytes between two programs. Each accepted connection is a TcpStream.

Open a server socket

bind claims the port. local_addr reports the one the OS picked.

let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();

Accept one client

accept waits for a client and hands you the stream and their address.

let (stream, addr) = listener.accept().unwrap();

Connect as a client

connect opens a stream to the server's address.

let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();

Streams are read and written like files

write_all sends. read_to_string waits for the reply.

client.write_all(b"hello tcp").unwrap();
let mut reply = String::new();
client.read_to_string(&mut reply).unwrap();

Example

use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    let server = thread::spawn(move || {
        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
        tx.send(listener.local_addr().unwrap().port()).unwrap();
        let (mut stream, _addr) = listener.accept().unwrap();
        let mut received = String::new();
        stream.read_to_string(&mut received).unwrap();
        let reply = received.to_uppercase();
        stream.write_all(reply.as_bytes()).unwrap();
        received
    });

    let port = rx.recv().unwrap();
    let mut client = TcpStream::connect(("127.0.0.1", port)).unwrap();
    client.write_all(b"hello tcp").unwrap();
    client.shutdown(std::net::Shutdown::Write).unwrap();
    let mut reply = String::new();
    client.read_to_string(&mut reply).unwrap();
    println!("reply = {reply}");
    let received = server.join().unwrap();
    println!("server received = {received}");
}

Key points

  • TCP lives in std::net.
  • A TcpListener accepts clients.
  • Each TcpStream is one live conversation.
  • A TcpStream implements Read and Write.
  • So it behaves like a file made of network.
  • Use TcpListener::bind("127.0.0.1:0") to take the first free port.
  • listener.local_addr() then tells you which port the operating system picked.
  • Use TcpStream::connect(("127.0.0.1", port)) to open the client side.
  • ToSocketAddrs also accepts names.
  • So connect("example.com:80") resolves the name first.
  • A TCP stream carries bytes, not messages.
  • Agree on an ending rule yourself.
  • This lesson closes the sending half with shutdown(Shutdown::Write).
  • That says "I am done writing".
  • The lesson then reads until the stream closes.
  • Use stream.set_read_timeout(Some(Duration::from_secs(2))) when a read must not wait forever.
  • The classic std pattern is one thread per client.
  • So many clients can be served at once.
  • std::net needs no extra crates and no Tokio runtime for a small TCP server.

The closing program runs a server thread and a client in main. They talk on the loopback address. The client sends hello tcp. The server replies in uppercase. Main prints the reply, then the server's received line.

Note

127.0.0.1 is this computer itself.

Every example here talks to its own machine.

That is the safe place to learn.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What does binding a TcpListener to port 0 do?