I/O Assíncrono
Tokio encapsula o I/O assíncrono do sistema operacional (epoll, kqueue, IOCP) para sockets e arquivos. Use as traits AsyncRead/AsyncWrite e helpers com buffer para código de rede e arquivo eficiente.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
stream.write_all(b"GET /\r\n\r\n").await?;
let mut buf = vec![0u8; 1024];
let n = stream.read(&mut buf).await?;
println!("leu {n} bytes");
Ok(())
}Quando usar isso: Servidores TCP/UDP, clientes HTTP, streaming de arquivos e qualquer protocolo baseado em socket.
Exemplo de Trabalho
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::fs::File;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let file = File::open("/etc/hosts").await?;
let mut reader = BufReader::new(file);
let mut line = String::new();
while reader.read_line(&mut line).await? > 0 {
if line.starts_with('#') {
line.clear();
continue;
}
println!("{}", line.trim());
line.clear();
}
Ok(())
}O que isso demonstra:
tokio::fs::Filepara abrir arquivos de forma assíncrona.BufReaderreduz a contagem de chamadas de sistema para leituras de linha.read_lineretorna 0 no final do arquivo (EOF).
Mergulho Profundo
Como Funciona
- Reactor: O sistema operacional notifica o Tokio quando sockets/arquivos estão legíveis/graváveis.
- AsyncRead/AsyncWrite:
poll_read/poll_writeintegram-se com os "wakers". - I/O com Buffer:
BufReader/BufWriteragrupam pequenas leituras/escritas. - Split:
split()separa as metades de leitura/escrita para direções concorrentes.
Tipos Comuns
| Tipo | Propósito |
|---|---|
TcpListener / TcpStream | Servidores e clientes TCP |
UdpSocket | Protocolos de datagrama |
tokio::fs | Sistema de arquivos assíncrono |
tokio::io::copy | Bomba de stream entre leitor/gravador |
Notas de Rust
use tokio::io::copy;
use tokio::fs::File;
async fn copy_file(src: &str, dst: &str) -> std::io::Result<u64> {
let mut reader = File::open(src).await?;
let mut writer = File::create(dst).await?;
copy(&mut reader, &mut writer).await
}- Prefira
reqwest/hyperpara HTTP em vez de TCP bruto para código de aplicação. - Defina
nodelaydo TCP e tamanhos de buffer para serviços sensíveis à latência. - Use
tokio::net::lookup_hostpara DNS assíncrono.
Armadilhas
- Pequenos loops de leitura sem buffer - Chamada de sistema por byte mata a taxa de transferência. Correção:
BufReaderou buffers fixos maiores. std::fsno manipulador assíncrono - Bloqueia o worker. Correção:tokio::fsouspawn_blocking.- Esquecer
flushnoBufWriter- Dados presos no buffer. Correção:flush().awaitantes de fechar. - Loop de
acceptsemspawn- Serializa conexões. Correção:spawnpor conexão ou use Axum. - Buffer de recebimento UDP muito pequeno - Trunca datagramas. Correção: Tamanho do buffer para MTU/máximo da aplicação.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Axum/hyper | APIs HTTP | Protocolo binário personalizado |
quinn | QUIC | Requisito de TCP simples |
mmap + sync | Arquivos estáticos grandes | Muitas leituras pequenas e concorrentes |
Bloqueio std em thread | Apenas legado | Caminho quente do worker Tokio |
FAQs
tokio::fs vs std::fs?
tokio::fs usa um pool de bloqueio nos bastidores para algumas operações, mas apresenta uma API assíncrona - ainda melhor do que chamar std diretamente nos workers.
AsyncRead em traits?
Use tokio::io::AsyncRead + async_trait ou limites genéricos em APIs de biblioteca.
TCP Half-close?
shutdown() nas metades do stream - dependente do protocolo para keep-alive HTTP.
Sockets Unix?
tokio::net::UnixStream espelha a API assíncrona TCP em plataformas Unix.
TLS?
tokio-rustls encapsula streams com handshake e leitura/escrita assíncronos.
Backpressure?
Pare de chamar read quando o downstream estiver cheio; use canais limitados entre os estágios.
Tamanho do buffer de copy?
Padrão de 8KB em copy - ajuste para pipelines de alta taxa de transferência.
stdio assíncrono?
tokio::io::stdin/stdout disponíveis - frequentemente em uma thread dedicada para CLI interativa.
Testando I/O?
Vincule 127.0.0.1:0 para porta efêmera; use tokio::test com sockets reais.
Relação com Axum?
Axum usa hyper + Tokio I/O - raramente precisa de TcpStream bruto em manipuladores web.
Relacionados
- Tokio Basics - Exemplo TCP
- Streams - Streams de bytes como Stream
- Blocking in Async - Armadilhas de I/O de arquivo
- select! & Concorrência - Multiplexação de I/O
- Graceful Shutdown - Drenagem de conexões
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.