Programação de Rede em Rust: escrevendo um servidor TCP rápido e seguro

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que Rust é o novo rei da programação de rede?

\n\n

Você já escreveu um servidor de rede em C ou C++ e pegou um segmentation fault por causa de um mutex esquecido? Ou lutou contra condições de corrida em Go, tentando entender por que uma goroutine "travou"? O Rust resolve esses problemas de uma vez por todas. Seu sistema de ownership e o modelo de concorrência "sem medo" (fearless concurrency) tornam a programação de rede em Rust não apenas segura, mas incrivelmente performática.

\n\n

Neste artigo, vamos explorar como criar servidores TCP e UDP em Rust, usando tanto a biblioteca padrão (std::net) quanto a moderna pilha async baseada em Tokio. Você verá que Rust não é apenas uma linguagem para programação de sistemas, mas também uma excelente ferramenta para escrever serviços de rede de alta carga.

\n\n

Fundamentos: std::net — servidor TCP echo em 5 minutos

\n\n

Vamos começar com o clássico — servidor echo. Ele aceita uma conexão TCP, lê os dados e os envia de volta ao cliente. Em Rust, isso é feito de forma surpreendentemente concisa.

\n\n
use std::io::{Read, Write};\nuse std::net::{TcpListener, TcpStream};\nuse std::thread;\n\nfn handle_client(mut stream: TcpStream) {\n    let mut buffer = [0; 1024];\n    loop {\n        match stream.read(&mut buffer) {\n            Ok(0) => {\n                println!("Cliente desconectou");\n                break;\n            }\n            Ok(n) => {\n                if let Err(e) = stream.write_all(&buffer[..n]) {\n                    eprintln!("Erro ao escrever: {}", e);\n                    break;\n                }\n            }\n            Err(e) => {\n                eprintln!("Erro ao ler: {}", e);\n                break;\n            }\n        }\n    }\n}\n\nfn main() -> std::io::Result<()> {\n    let listener = TcpListener::bind("127.0.0.1:7878")?;\n    println!("Servidor iniciado na porta 7878");\n\n    for stream in listener.incoming() {\n        match stream {\n            Ok(stream) => {\n                thread::spawn(|| handle_client(stream));\n            }\n            Err(e) => {\n                eprintln!("Erro de conexão: {}", e);\n            }\n        }\n    }\n    Ok(())\n}\n
\n\n

O que está acontecendo aqui? TcpListener::bind cria um socket de escuta. O método incoming() retorna um iterador sobre as conexões recebidas. Para cada novo cliente, iniciamos uma thread separada — thread::spawn. Dentro de handle_client, lemos os dados para um buffer e os enviamos de volta via write_all.

\n\n

Importante: Rust garante que TcpStream não será acidentalmente usado por duas threads simultaneamente — o compilador simplesmente não permite. Isso é o fearless concurrency.

\n\n

Problemas dessa abordagem

\n
    \n
  • Escalabilidade: cada cliente é uma thread separada. Com 10.000 clientes, o sistema operacional simplesmente trava.
  • \n
  • Recursos: a pilha de thread padrão tem 2 MB. 10.000 clientes = 20 GB de memória.
  • \n
\n\n

A programação assíncrona vem para ajudar.

\n\n

Revolução Async: Tokio e async/await

\n\n

Rust não possui um runtime assíncrono embutido (como Go ou Node.js). Em vez disso, ele fornece os traits Future e as palavras-chave async/await, e a escolha do runtime é sua. O mais popular é o Tokio. Não é apenas uma biblioteca, é um ecossistema completo: TCP/UDP, E/S de arquivos, timers, canais e até seu próprio escalonador.

\n\n

Vamos reescrever nosso servidor echo com Tokio.

\n\n

Servidor echo com Tokio

\n

Adicione ao Cargo.toml:

\n
[dependencies]\ntokio = { version = "1", features = ["full"] }
\n\n

Agora o código:

\n\n
use tokio::io::{AsyncReadExt, AsyncWriteExt};\nuse tokio::net::TcpListener;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n                

Blogs

Book Recommendations