Bibliotecas de Rede em Zig — TCP, UDP, Sockets e Protocolos
A programação de rede é uma das áreas onde o Zig mais brilha. Com controle fino de memória, ausência de garbage collector e acesso direto a syscalls do sistema operacional, o Zig permite construir aplicações de rede com performance comparável a C, mas com segurança e ergonomia muito superiores. O ecossistema oferece tanto funcionalidades na biblioteca padrão quanto bibliotecas de terceiros especializadas.
Rede na Biblioteca Padrão
A biblioteca padrão do Zig (std.net) inclui suporte robusto para programação de rede:
Servidor TCP Básico
const std = @import("std");
const net = std.net;
pub fn main() !void {
// Criar servidor TCP
var server = try net.StreamServer.init(.{
.reuse_address = true,
});
defer server.deinit();
// Bind na porta 8080
try server.listen(net.Address.parseIp4("0.0.0.0", 8080) catch unreachable);
std.debug.print("Servidor ouvindo na porta 8080\n", .{});
while (true) {
// Aceitar conexão
const connection = try server.accept();
defer connection.stream.close();
// Ler dados
var buf: [1024]u8 = undefined;
const bytes_read = try connection.stream.read(&buf);
if (bytes_read > 0) {
std.debug.print("Recebido: {s}\n", .{buf[0..bytes_read]});
// Enviar resposta
try connection.stream.writeAll("Mensagem recebida!\n");
}
}
}
Cliente TCP
const std = @import("std");
const net = std.net;
pub fn main() !void {
// Conectar ao servidor
const stream = try net.tcpConnectToHost(
std.heap.page_allocator,
"localhost",
8080,
);
defer stream.close();
// Enviar dados
try stream.writeAll("Olá, servidor!\n");
// Ler resposta
var buf: [1024]u8 = undefined;
const bytes_read = try stream.read(&buf);
std.debug.print("Resposta: {s}\n", .{buf[0..bytes_read]});
}
Resolução DNS
const std = @import("std");
const net = std.net;
pub fn main() !void {
const allocator = std.heap.page_allocator;
// Resolver hostname
const addresses = try net.getAddressList(allocator, "ziglang.org", 443);
defer addresses.deinit();
for (addresses.addrs) |addr| {
var buf: [64]u8 = undefined;
const ip_str = try std.fmt.bufPrint(&buf, "{}", .{addr});
std.debug.print("Endereço: {s}\n", .{ip_str});
}
}
UDP
const std = @import("std");
const net = std.net;
const posix = std.posix;
pub fn main() !void {
// Criar socket UDP
const sock = try posix.socket(
posix.AF.INET,
posix.SOCK.DGRAM,
0,
);
defer posix.close(sock);
// Bind
const addr = net.Address.parseIp4("0.0.0.0", 9090) catch unreachable;
try posix.bind(sock, &addr.any, addr.getOsSockLen());
std.debug.print("Servidor UDP na porta 9090\n", .{});
var buf: [1024]u8 = undefined;
while (true) {
var src_addr: posix.sockaddr = undefined;
var addr_len: posix.socklen_t = @sizeOf(posix.sockaddr);
const bytes = try posix.recvfrom(
sock,
&buf,
0,
&src_addr,
&addr_len,
);
std.debug.print("UDP recebido: {s}\n", .{buf[0..bytes]});
// Responder
_ = try posix.sendto(
sock,
"ACK",
0,
&src_addr,
addr_len,
);
}
}
Bibliotecas de Rede de Terceiros
zig-network
Biblioteca que estende as capacidades de rede da std com abstrações de alto nível:
const network = @import("zig-network");
pub fn main() !void {
var socket = try network.Socket.create(.ipv4, .tcp);
defer socket.close();
try socket.connect(.{
.address = try network.Address.parse("93.184.216.34"),
.port = 80,
});
try socket.send("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
var buf: [4096]u8 = undefined;
const received = try socket.receive(&buf);
std.debug.print("Resposta HTTP: {s}\n", .{buf[0..received]});
}
zig-aio
Framework de I/O assíncrono baseado em io_uring (Linux) e kqueue (macOS):
const aio = @import("zig-aio");
pub fn main() !void {
var ring = try aio.IoUring.init(256);
defer ring.deinit();
// Aceitar conexões assincronamente
var server = try aio.TcpListener.bind("0.0.0.0", 8080);
while (true) {
const conn = try server.acceptAsync(&ring);
// Processar conexão sem bloquear
try ring.submit();
try handleConnection(conn);
}
}
zzz (zigzag)
Servidor HTTP/1.1 e HTTP/2 de alta performance com suporte a io_uring:
const zzz = @import("zzz");
pub fn main() !void {
var server = try zzz.Server.init(.{
.port = 3000,
.num_threads = 8,
.use_io_uring = true,
});
server.route("/", struct {
fn handler(req: *zzz.Request, res: *zzz.Response) !void {
_ = req;
try res.send("Hello from zzz!");
}
}.handler);
try server.start();
}
I/O Assíncrono com io_uring
O Zig possui suporte experimental a io_uring, a API de I/O assíncrono mais eficiente do Linux:
const std = @import("std");
const linux = std.os.linux;
pub fn main() !void {
// Inicializar io_uring
var ring: linux.IoUring = undefined;
const ret = linux.io_uring_setup(256, &ring.params);
if (ret < 0) return error.IoUringSetupFailed;
// Submeter operação de leitura assíncrona
var sqe = ring.get_sqe() orelse return error.SubmissionQueueFull;
sqe.prep_read(fd, &buffer, buffer.len, 0);
// Submeter e aguardar conclusão
_ = try ring.submit();
var cqe = try ring.wait_cqe();
ring.cqe_seen(cqe);
}
Protocolos de Rede
Implementação de Protocolo Customizado
O Zig é excelente para implementar protocolos binários:
const std = @import("std");
// Definir formato do pacote
const Pacote = packed struct {
versao: u8,
tipo: TipoPacote,
tamanho: u16,
sequence_number: u32,
checksum: u32,
const TipoPacote = enum(u8) {
handshake = 0x01,
dados = 0x02,
ack = 0x03,
heartbeat = 0x04,
encerramento = 0xFF,
};
fn calcularChecksum(self: *const Pacote, payload: []const u8) u32 {
var hash: u32 = 0;
for (payload) |byte| {
hash = hash *% 31 +% byte;
}
hash +%= self.sequence_number;
return hash;
}
fn serializar(self: *const Pacote) [@sizeOf(Pacote)]u8 {
return @bitCast(self.*);
}
fn deserializar(bytes: [@sizeOf(Pacote)]u8) Pacote {
return @bitCast(bytes);
}
};
Pool de Conexões
const ConnectionPool = struct {
connections: std.ArrayList(Connection),
max_size: usize,
mutex: std.Thread.Mutex,
const Connection = struct {
stream: std.net.Stream,
em_uso: bool,
ultima_atividade: i64,
};
pub fn obter(self: *ConnectionPool) !*Connection {
self.mutex.lock();
defer self.mutex.unlock();
for (self.connections.items) |*conn| {
if (!conn.em_uso) {
conn.em_uso = true;
conn.ultima_atividade = std.time.timestamp();
return conn;
}
}
if (self.connections.items.len < self.max_size) {
// Criar nova conexão
const stream = try std.net.tcpConnectToHost(
std.heap.page_allocator,
"localhost",
5432,
);
try self.connections.append(.{
.stream = stream,
.em_uso = true,
.ultima_atividade = std.time.timestamp(),
});
return &self.connections.items[self.connections.items.len - 1];
}
return error.PoolExhausted;
}
pub fn devolver(self: *ConnectionPool, conn: *Connection) void {
self.mutex.lock();
defer self.mutex.unlock();
conn.em_uso = false;
}
};
Casos de Uso em Produção
As bibliotecas de rede do Zig são usadas em projetos significativos:
- Bun usa networking Zig para seu runtime HTTP
- TigerBeetle usa protocolos customizados para replicação de banco de dados
- Cloudflare utiliza Zig para serviços de edge computing
Boas Práticas
- Use alocadores apropriados:
ArenaAllocatorpara conexões curtas,GeneralPurposeAllocatorpara servidores long-running - Implemente timeouts: Sempre defina timeouts para operações de rede
- Trate erros explicitamente: O sistema de erros do Zig facilita o tratamento robusto de falhas de rede
- Use pools de conexão: Reutilize conexões TCP para melhor performance
- Considere io_uring: Para aplicações de alta carga no Linux, io_uring oferece performance superior
Próximos Passos
Explore os frameworks web para construir APIs, os clientes HTTP para consumir serviços, e as bibliotecas de criptografia para comunicações seguras. Para exemplos práticos, consulte nossas receitas e tutoriais.