std.net.Address — Endereços de Rede
O tipo std.net.Address representa um endereço de rede completo (endereço IP e porta) usado para identificar endpoints em comunicações de rede. Ele suporta IPv4, IPv6 e Unix domain sockets, e fornece funções para parsing, formatação e resolução de nomes DNS.
Visão Geral
const std = @import("std");
const net = std.net;
const Address = net.Address;
Estrutura
pub const Address = union(enum) {
ipv4: std.posix.sockaddr.in,
ipv6: std.posix.sockaddr.in6,
un: std.posix.sockaddr.un,
};
O tipo é uma tagged union que pode representar:
- IPv4: endereço de 32 bits + porta de 16 bits
- IPv6: endereço de 128 bits + porta de 16 bits + flow info + scope id
- Unix: caminho do socket no sistema de arquivos
Funções Principais
Criação de Endereços
// Parse de string IPv4
pub fn parseIp4(ip: []const u8, port: u16) !Address
// Parse de string IPv6
pub fn parseIp6(ip: []const u8, port: u16) !Address
// Parse automático (detecta IPv4/IPv6)
pub fn parseIp(ip: []const u8, port: u16) !Address
// Resolve hostname via DNS
pub fn resolveIp(name: []const u8, port: u16) !Address
// Endereço para "qualquer" interface (0.0.0.0 ou ::)
pub fn initIp4(addr: [4]u8, port: u16) Address
pub fn initIp6(addr: [16]u8, port: u16) Address
// Unix domain socket
pub fn initUnix(path: []const u8) !Address
Acesso a Componentes
// Retorna a porta
pub fn getPort(self: Address) u16
// Define a porta
pub fn setPort(self: *Address, port: u16) void
// Retorna o endereço como bytes
pub fn getOsSockLen(self: Address) std.posix.socklen_t
Comparação e Formatação
// Compara dois endereços
pub fn eql(a: Address, b: Address) bool
// Formata para impressão
pub fn format(self: Address, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void
Operações de Socket
// Cria um servidor TCP neste endereço
pub fn listen(self: Address, options: ListenOptions) !net.Server
// Conecta a este endereço via TCP
pub fn tcpConnectToAddress(address: Address) !net.Stream
Exemplo 1: Trabalhando com Endereços IPv4 e IPv6
const std = @import("std");
const net = std.net;
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// IPv4
const addr_local = try net.Address.parseIp4("127.0.0.1", 8080);
const addr_rede = try net.Address.parseIp4("192.168.1.100", 3000);
const addr_qualquer = net.Address.initIp4(.{ 0, 0, 0, 0 }, 9000);
try stdout.print("Local: {}\n", .{addr_local});
try stdout.print("Rede: {}\n", .{addr_rede});
try stdout.print("Qualquer: {}\n", .{addr_qualquer});
// IPv6
const addr_loopback6 = try net.Address.parseIp6("::1", 8080);
const addr_qualquer6 = try net.Address.parseIp6("::", 9000);
try stdout.print("IPv6 loopback: {}\n", .{addr_loopback6});
try stdout.print("IPv6 qualquer: {}\n", .{addr_qualquer6});
// Parse automático
const auto4 = try net.Address.parseIp("10.0.0.1", 80);
const auto6 = try net.Address.parseIp("::1", 80);
try stdout.print("Auto IPv4: {}\n", .{auto4});
try stdout.print("Auto IPv6: {}\n", .{auto6});
// Porta
try stdout.print("Porta: {d}\n", .{addr_local.getPort()});
}
Exemplo 2: Resolução DNS e Verificação
const std = @import("std");
const net = std.net;
const ServicoInfo = struct {
nome: []const u8,
host: []const u8,
porta: u16,
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const servicos = [_]ServicoInfo{
.{ .nome = "Web Local", .host = "127.0.0.1", .porta = 80 },
.{ .nome = "API", .host = "127.0.0.1", .porta = 3000 },
.{ .nome = "Banco", .host = "127.0.0.1", .porta = 5432 },
.{ .nome = "Cache", .host = "127.0.0.1", .porta = 6379 },
};
try stdout.writeAll("=== Endereços dos Serviços ===\n\n");
for (servicos) |servico| {
const addr = net.Address.resolveIp(servico.host, servico.porta) catch |err| {
try stdout.print("{s:<12} ERRO: {}\n", .{ servico.nome, err });
continue;
};
try stdout.print("{s:<12} {}\n", .{ servico.nome, addr });
}
// Compara endereços
try stdout.writeAll("\n=== Comparações ===\n");
const a = try net.Address.parseIp4("127.0.0.1", 80);
const b = try net.Address.parseIp4("127.0.0.1", 80);
const c = try net.Address.parseIp4("127.0.0.1", 443);
const d = try net.Address.parseIp4("192.168.1.1", 80);
try stdout.print("127.0.0.1:80 == 127.0.0.1:80: {}\n", .{a.eql(b)});
try stdout.print("127.0.0.1:80 == 127.0.0.1:443: {}\n", .{a.eql(c)});
try stdout.print("127.0.0.1:80 == 192.168.1.1:80: {}\n", .{a.eql(d)});
}
Exemplo 3: Servidor Multi-Endereço
const std = @import("std");
const net = std.net;
const EndpointConfig = struct {
nome: []const u8,
ip: []const u8,
porta: u16,
};
fn formatarEnderecos(
configs: []const EndpointConfig,
allocator: std.mem.Allocator,
) !std.ArrayList(net.Address) {
var enderecos = std.ArrayList(net.Address).init(allocator);
errdefer enderecos.deinit();
for (configs) |cfg| {
const addr = net.Address.parseIp(cfg.ip, cfg.porta) catch |err| {
std.debug.print("Erro em '{s}': {}\n", .{ cfg.nome, err });
continue;
};
try enderecos.append(addr);
}
return enderecos;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const configs = [_]EndpointConfig{
.{ .nome = "HTTP", .ip = "0.0.0.0", .porta = 80 },
.{ .nome = "HTTPS", .ip = "0.0.0.0", .porta = 443 },
.{ .nome = "Admin", .ip = "127.0.0.1", .porta = 9090 },
.{ .nome = "Métricas", .ip = "127.0.0.1", .porta = 9091 },
};
var enderecos = try formatarEnderecos(&configs, allocator);
defer enderecos.deinit();
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("Endpoints configurados:\n");
for (configs[0..enderecos.items.len], enderecos.items) |cfg, addr| {
try stdout.print(" {s:<10} -> {}\n", .{ cfg.nome, addr });
}
// Cria endereço para binding em todas as interfaces
const bind_all = net.Address.initIp4(.{ 0, 0, 0, 0 }, 8080);
try stdout.print("\nBind all: {}\n", .{bind_all});
try stdout.print("É IPv4: {}\n", .{bind_all == .ipv4});
}
Módulos Relacionados
- std.net — Visão geral de networking
- std.net.Stream — Conexão TCP
- std.http — HTTP
- std.Uri — Parsing de URIs