Fundamentos de Programação TCP em Rust
Rust oferece recursos avançados para programação de rede através do módulo std::net, com destaque para:
TcpListener: Para aceitar conexões de clientesTcpStream: Para comunicação bidirecional
Exemplo de Servidor TCP Básico
use std::net::{TcpListener, TcpStream};
use std::io::{self, Read, Write};
fn main() -> io::Result<()> {
let servidor = TcpListener::bind("0.0.0.0:7878")?;
for conexao in servidor.incoming() {
let mut conexao = conexao?;
manipular_conexao(&mut conexao)?;
}
Ok(())
}
fn manipular_conexao(conexao: &mut TcpStream) -> io::Result<()> {
let mut dados = [0; 512];
let bytes_lidos = conexao.read(&mut dados)?;
conexao.write_all(&dados[..bytes_lidos])?;
Ok(())
}
Problemas de Desempenho e Soluções
1. Bloqueio de I/O e Gerenciamento de Threads
O modelo síncrono tradicional cria uma thread por conexão, levando a:
| Conexões | Threads | Uso de Memória |
|---|---|---|
| 1,000 | 1,000 | ~1GB |
| 10,000 | 10,000 | ~10GB |
2. Dimensionamento de Buffer
Buffers mal dimansionados impactam o throughput:
// Buffer pequeno (1KB) - ineficiente
let mut buffer_pequeno = [0; 1024];
conexao.read(&mut buffer_pequeno)?;
// Buffer otimizado (8KB)
let mut buffer_otimizado = [0; 8192];
conexao.read(&mut buffer_otimizado)?;
3. Vazamento de Conexões
Exemplo de má prática:
let conexao = TcpStream::connect("127.0.0.1:8080")?;
// Esqueceu de fechar a conexão
Solução recomnedada:
{
let conexao = TcpStream::connect("127.0.0.1:8080")?;
// Conexão será fechada automaticamente ao sair do escopo
}
Programação Assíncrona com Tokio
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::error="">> {
let mut conexao = TcpStream::connect("127.0.0.1:8080").await?;
conexao.write_all(b"Olá Tokio!").await?;
Ok(())
}</dyn>
Comparativo de Modelos
| Característica | Síncrono | Assíncrono |
|---|---|---|
| Throughput | Baixo | Alto |
| Uso de Recursos | Alto | Eficiente |
Técnicas Avançadas
Zero-Copy
Reduz cópias desnecessárias entre espaço de usuário e kernel:
use std::os::unix::io::AsRawFd;
use nix::sys::sendfile;
let arquivo = std::fs::File::open("dados.bin")?;
let bytes_enviados = sendfile(
conexao.as_raw_fd(),
arquivo.as_raw_fd(),
None,
1024
)?;
Controle de Fluxo
Implementando backpressure:
use tokio::sync::Semaphore;
let semaforo = Semaphore::new(10); // Limite de 10 conexões simultâneas
async fn processar(conexao: TcpStream, _permit: SemaphorePermit) {
// Lógica de processamento
}
let manipulador = async {
let permit = semaforo.acquire().await.unwrap();
processar(conexao, permit).await;
};