Bibliotecas de Rede em Zig — TCP, UDP, Sockets e Protocolos

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

  1. Use alocadores apropriados: ArenaAllocator para conexões curtas, GeneralPurposeAllocator para servidores long-running
  2. Implemente timeouts: Sempre defina timeouts para operações de rede
  3. Trate erros explicitamente: O sistema de erros do Zig facilita o tratamento robusto de falhas de rede
  4. Use pools de conexão: Reutilize conexões TCP para melhor performance
  5. 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.

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.