std.net.Stream — Conexão TCP Bidirecional
O std.net.Stream representa uma conexão TCP estabelecida e bidirecional. Ele encapsula um descritor de socket do sistema operacional e fornece métodos para leitura e escrita de dados, além de implementar as interfaces Reader e Writer do std.io, permitindo usar todas as funções de I/O genéricas do Zig.
Visão Geral
const std = @import("std");
const net = std.net;
const Stream = net.Stream;
Estrutura
pub const Stream = struct {
handle: std.posix.socket_t,
pub fn read(self: Stream, buffer: []u8) ReadError!usize
pub fn readv(self: Stream, iovecs: []std.posix.iovec) ReadError!usize
pub fn write(self: Stream, buffer: []const u8) WriteError!usize
pub fn writev(self: Stream, iovecs: []std.posix.iovec_const) WriteError!usize
pub fn close(self: Stream) void
pub fn reader(self: Stream) std.io.GenericReader(...)
pub fn writer(self: Stream) std.io.GenericWriter(...)
};
O Stream é um tipo leve (apenas um handle) que pode ser copiado livremente. Chamar close() em qualquer cópia fecha a conexão para todas.
Funções Principais
Leitura
// Lê bytes no buffer, retorna quantidade lida (0 = fim)
pub fn read(self: Stream, buffer: []u8) ReadError!usize
// Lê usando scatter (múltiplos buffers)
pub fn readv(self: Stream, iovecs: []std.posix.iovec) ReadError!usize
// Retorna Reader compatível com std.io
pub fn reader(self: Stream) Reader
Escrita
// Escreve bytes, retorna quantidade escrita
pub fn write(self: Stream, buffer: []const u8) WriteError!usize
// Escreve usando gather (múltiplos buffers)
pub fn writev(self: Stream, iovecs: []std.posix.iovec_const) WriteError!usize
// Retorna Writer compatível com std.io
pub fn writer(self: Stream) Writer
Controle
// Fecha o socket
pub fn close(self: Stream) void
// Obtém informações do socket
pub fn getLocalAddress(self: Stream) !net.Address
pub fn getRemoteAddress(self: Stream) !net.Address
Exemplo 1: Cliente que Envia e Recebe Dados
const std = @import("std");
const net = std.net;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const stdout = std.io.getStdOut().writer();
// Conecta ao servidor
const stream = net.tcpConnectToHost(allocator, "example.com", 80) catch |err| {
try stdout.print("Falha na conexão: {}\n", .{err});
return;
};
defer stream.close();
// Usa a interface Writer para enviar HTTP
const writer = stream.writer();
try writer.writeAll("GET / HTTP/1.1\r\n");
try writer.writeAll("Host: example.com\r\n");
try writer.writeAll("Connection: close\r\n");
try writer.writeAll("\r\n");
// Usa a interface Reader para ler a resposta
const reader = stream.reader();
var buf: [4096]u8 = undefined;
// Lê primeira linha (status HTTP)
if (try reader.readUntilDelimiterOrEof(&buf, '\n')) |linha| {
try stdout.print("Status: {s}\n", .{std.mem.trim(u8, linha, "\r")});
}
// Conta bytes totais da resposta
var total_bytes: usize = 0;
while (true) {
const n = try reader.read(&buf);
if (n == 0) break;
total_bytes += n;
}
try stdout.print("Total recebido: {d} bytes\n", .{total_bytes});
}
Exemplo 2: Protocolo de Mensagens com Tamanho Prefixado
const std = @import("std");
const net = std.net;
const Protocolo = struct {
stream: net.Stream,
// Envia mensagem com prefixo de tamanho (4 bytes, big-endian)
pub fn enviarMensagem(self: Protocolo, dados: []const u8) !void {
const writer = self.stream.writer();
const tamanho: u32 = @intCast(dados.len);
// Escreve o tamanho primeiro
try writer.writeInt(u32, tamanho, .big);
// Depois os dados
try writer.writeAll(dados);
}
// Recebe mensagem com prefixo de tamanho
pub fn receberMensagem(self: Protocolo, buf: []u8) !?[]u8 {
const reader = self.stream.reader();
// Lê o tamanho (4 bytes)
const tamanho = reader.readInt(u32, .big) catch |err| {
if (err == error.EndOfStream) return null;
return err;
};
if (tamanho > buf.len) return error.BufferMuitoPequeno;
// Lê exatamente 'tamanho' bytes
const slice = buf[0..tamanho];
try reader.readNoEof(slice);
return slice;
}
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Demonstra o protocolo com um fixedBufferStream (simulação)
var buf_dados: [1024]u8 = undefined;
var stream_buf = std.io.fixedBufferStream(&buf_dados);
// Simula escrita de mensagem
const writer = stream_buf.writer();
const mensagem = "Olá, protocolo!";
const tamanho: u32 = @intCast(mensagem.len);
try writer.writeInt(u32, tamanho, .big);
try writer.writeAll(mensagem);
// Simula leitura
stream_buf.reset();
const reader = stream_buf.reader();
const tam_lido = try reader.readInt(u32, .big);
var buf_recv: [256]u8 = undefined;
try reader.readNoEof(buf_recv[0..tam_lido]);
try stdout.print("Mensagem recebida ({d} bytes): {s}\n", .{
tam_lido,
buf_recv[0..tam_lido],
});
}
Exemplo 3: Proxy TCP Simples
const std = @import("std");
const net = std.net;
fn encaminhar(origem: net.Stream, destino: net.Stream) void {
var buf: [4096]u8 = undefined;
while (true) {
const n = origem.read(&buf) catch break;
if (n == 0) break;
destino.writer().writeAll(buf[0..n]) catch break;
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const porta_local: u16 = 9000;
const host_destino = "example.com";
const porta_destino: u16 = 80;
const addr = try net.Address.parseIp4("127.0.0.1", porta_local);
var server = try addr.listen(.{ .reuse_address = true });
defer server.deinit();
std.debug.print("Proxy escutando em :{d} -> {s}:{d}\n", .{
porta_local, host_destino, porta_destino,
});
while (true) {
const conn = server.accept() catch continue;
std.debug.print("Nova conexão de {}\n", .{conn.address});
// Conecta ao destino
const destino = net.tcpConnectToHost(
allocator,
host_destino,
porta_destino,
) catch |err| {
std.debug.print("Erro ao conectar ao destino: {}\n", .{err});
conn.stream.close();
continue;
};
// Em produção, use threads separadas para cada direção
encaminhar(conn.stream, destino);
destino.close();
conn.stream.close();
}
}
Módulos Relacionados
- std.net — Visão geral de networking
- std.net.Address — Tipos de endereço
- std.io.Reader — Interface Reader
- std.io.Writer — Interface Writer
- std.http — HTTP sobre Stream