TCP server with tokio::spawn
Three lines of substance: TcpListener::bind opens the port. accept loops. tokio::spawn launches a task per connection. Done.
#[tokio::main]
async fn main() -> io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:6380").await?;
println!("echo server listening on 127.0.0.1:6380");
loop {
let (socket, _addr) = listener.accept().await?;
tokio::spawn(async move { let _ = handle_conn(socket).await; });
}
}
async fn handle_conn(mut socket: TcpStream) -> io::Result<()> {
let mut buf = [0u8; 4096];
loop {
let n = socket.read(&mut buf).await?;
if n == 0 { return Ok(()); }
socket.write_all(&buf[..n]).await?;
}
}The entire echo server. tokio tasks are cheap (~64 bytes), so per-conn spawning is the standard idiom.
Both spawn a unit of work per connection. Go goroutines are ~2 KB stacks scheduled by the Go runtime; tokio tasks are state machines compiled from async fn, much smaller (~64 bytes), scheduled by the tokio runtime. The architecture is identical; tokio is closer to a stackless coroutine model.
Quiz: Quiz
Loading practice…