std.net — Netzwerk

Zurück zur Unit-Übersicht

Netzwerk-Namensraum für TCP/UDP-Kommunikation, Anwendungsprotokolle und Netzwerkmanagement. Alle Units implementieren ihre Protokolle nativ über Syscalls — ohne externe Bibliotheken. Ausnahmen: std.net.tls nutzt OpenSSL, std.net.ssh nutzt libssh2.

Diese Seite ist die Übersicht über den Namensraum: Schichtaufbau, Kernbegriffe und lauffähige Einstiegsbeispiele. Die vollständige Funktionsreferenz steht auf den verlinkten Unterseiten.

std.net selbst ist keine Unit, sondern ein Verzeichnis — import std.net; scheitert mit Modul nicht gefunden. Importiert wird immer eine konkrete Unit wie std.net.socket.

Quelle: std/net/

<WRAP info> TCPListenerAccept arbeitet seit 1.0.20F (#1451) — nachgemessen mit einer echten Verbindung auf 127.0.0.1:18777: der Aufruf nimmt an und liefert eine benutzbare TCPConn. Der frühere Umweg über AcceptBlocking(srv.fd, 0, 0) ist nicht mehr nötig. </WRAP>

Unit-Übersicht

Unit Gruppe Beschreibung
std.net.socket Transport TCP, UDP, Unix-Domain, Raw-Sockets, ICMP, ARP, poll/select, IPv6
std.net.types Transport Adressstrukturen (SockAddrInCreate), IP-Parsing, Byte-Order, IPv6-Helfer
std.net.tls Transport TLS/SSL über OpenSSL
std.net.syscalls Transport Portable POSIX-Socket-Syscalls (unterste Schicht)
std.net.epoll Transport epoll-Multiplexing: EpollCreate, EpollAdd/Mod/Del, EpollWait, EventFd*
std.net.dns Protokolle DNS-Auflösung (A, AAAA, MX, NS, TXT, SOA, SRV, CAA, DNSKEY, DS, PTR), DoH, Cache
std.net.http Protokolle HTTP/1.1-Client: GET, POST, PUT, DELETE, PATCH, HEAD, Redirects, Auth-Header
std.net.https Protokolle HTTPS-Client (TLS davor)
std.net.smtp Protokolle SMTP-Mailversand (Ports 25/587/465)
std.net.imap Protokolle IMAP4rev1-Postfachzugriff: SELECT, LIST, FETCH
std.net.ssh Protokolle SSH: Auth, Remote-Befehle, Output-Capture (libssh2)
std.net.mqtt Protokolle MQTT 3.1.1: Publish, Subscribe, QoS 0/1/2
std.net.ntp Protokolle NTP v3: Zeitabfrage via UDP Port 123
std.net.dhcp Protokolle DHCP-Client (RFC 2131): DORA-Lease, Paket-Builder, Reply-Parser
std.net.arp Protokolle ARP (RFC 826): Request/Reply, Frame-Parser, Kernel-Cache via SIOCGARP
std.net.eth / ipv4 / udp Protokolle Reine Frame-Builder ohne Syscalls und ohne Allokation — kernel-tauglich
std.net.quic Protokolle QUIC (RFC 9000): UDP-basiert, TLS 1.3 integriert
std.net.sip Protokolle SIP (RFC 3261): REGISTER, INVITE, OPTIONS
std.net.telnet Protokolle Telnet (RFC 854): Option-Negotiation
std.net.whois Protokolle WHOIS (RFC 3912): Auto-Routing zur Registry
std.net.ldap Verzeichnis & Routing LDAP (RFC 4511): Bind, Search, Modify, SASL
std.net.bgp Verzeichnis & Routing BGP-4 (RFC 4271): AS-Routing, UPDATE-Messages
std.net.snmp Management SNMPv1/v2c: GET, SET, WALK über OIDs
std.net.asn1 Management ASN.1/BER-Kodierung (Basis für SNMP)
std.net.mongo Datenbank MongoDB Wire Protocol: BSON, SCRAM-SHA-1

Schichtarchitektur

┌─────────────────────────────────────────────────────────┐
│  Anwendungsprotokolle                                   │
│  http · https · smtp · imap · ssh · mqtt · dns · …      │
├─────────────────────────────────────────────────────────┤
│  Transport                                              │
│  socket · tls · epoll · types                           │
├─────────────────────────────────────────────────────────┤
│  Syscalls                                               │
│  syscalls  (sys_socket, sys_connect, sys_send, …)       │
└─────────────────────────────────────────────────────────┘

Beim direkten Einsatz importiert man nur die Protokoll-Unit — die Transportschicht wird als Abhängigkeit mitgezogen. Wer selbst auf Socket-Ebene arbeitet, braucht in der Regel beide: std.net.socket für die Operationen und std.net.types für die Adressstruktur.


Kernbegriffe

Drei Dinge unterscheiden diese Units von einer Socket-API, wie man sie aus C oder höheren Sprachen kennt:

Puffer sind rohe Adressen, Text ist pchar. Lese- und Schreibfunktionen nehmen int64-Zeiger auf Binärpuffer; die werden mit BufferAlloc(n) geholt und mit BufferFree zurückgegeben, ein gelesener Puffer wird mit as pchar ausgegeben. Zeichenketten-Parameter sind seit #1615 als pchar deklariertHTTPGet, HTTPSetHeader, RestClientInit und die übrigen 13 umgestellten Funktionen brauchen kein as int64 mehr.

Adressen sind vorbereitete Strukturen. bind und sendto erwarten einen fertigen sockaddr_in-Block. Der wird mit BufferAlloc(SOCKADDR_IN_SIZE) geholt und mit SockAddrInCreate(addr, ip, port) gefüllt; die IP entsteht aus IPPack(a, b, c, d).

Handles sind kleine Structs. TCPListener, TCPConn und UDPSocket enthalten je ein Feld fd. Negativer fd bedeutet Fehler — jeder Rückgabewert ist zu prüfen, es gibt keine Ausnahmen.


Beispiele

TCP — Server und Client

Beide Seiten in einem Prozess: der Listener nimmt die Verbindung über die Listen-Queue an, danach wird in beide Richtungen geschrieben und gelesen.

import std.net.socket;
import std.net.types;

fn main(): int64 {
    var srv: TCPListener := TCPListenerNew();
    var addr: int64 := BufferAlloc(SOCKADDR_IN_SIZE);
    var ip: int64 := IPPack(127, 0, 0, 1);
    SockAddrInCreate(addr, ip, 18086);
    TCPListenerBind(srv, addr);
    TCPListenerListen(srv, 16);

    var cli: TCPConn := TCPConnect(ip, 18086);
    PrintLn("Client verbunden (fd ", IntToStr(cli.fd), ")");

    // Umweg ueber AcceptBlocking: liefert einen rohen fd statt eines Structs
    var connFd: int64 := AcceptBlocking(srv.fd, 0, 0);
    PrintLn("Server akzeptiert (fd ", IntToStr(connFd), ")");

    var conn: TCPConn;
    conn.fd := connFd;

    TCPConnWrite(cli, "PING", 4);
    var buf: int64 := BufferAlloc(64);
    var n: int64 := TCPConnRead(conn, buf as pchar, 64);
    PrintLn("Server las ", IntToStr(n), " Bytes: ", buf as pchar);

    TCPConnWrite(conn, "PONG", 4);
    var buf2: int64 := BufferAlloc(64);
    var m: int64 := TCPConnRead(cli, buf2 as pchar, 64);
    PrintLn("Client las ", IntToStr(m), " Bytes: ", buf2 as pchar);

    TCPConnClose(conn);
    TCPConnClose(cli);
    TCPListenerClose(srv);
    PrintLn("Verbindungen geschlossen");
    return 0;
}

Ausgabe:

Client verbunden (fd 4)
Server akzeptiert (fd 5)
Server las 4 Bytes: PING
Client las 4 Bytes: PONG
Verbindungen geschlossen

Das Beispiel nimmt AcceptBlocking(srv.fd, 0, 0) und setzt den rohen Dateideskriptor von Hand in ein TCPConn. Seit 1.0.20F geht auch TCPListenerAccept(srv) unmittelbar (#1451) — nachgemessen mit einer echten Verbindung; die kürzere Form ist für neuen Code vorzuziehen.

Für einen Server, der auf mehrere Verbindungen wartet, gehört zusätzlich TCPListenerSetReuseAddr(srv) vor den Bind-Aufruf — sonst scheitert ein Neustart am TIME_WAIT-Zustand des Ports.

UDP — senden und empfangen

import std.net.socket;
import std.net.types;

fn main(): int64 {
    var ip: int64 := IPPack(127, 0, 0, 1);

    // Empfaenger
    var rx: UDPSocket := UDPSocketNew();
    var raddr: int64 := BufferAlloc(SOCKADDR_IN_SIZE);
    SockAddrInCreate(raddr, ip, 19090);
    if (UDPSocketBind(rx, raddr) < 0) { PrintLn("bind fehlgeschlagen"); return 1; }
    PrintLn("Empfaenger gebunden an 127.0.0.1:19090");

    // Sender
    var tx: UDPSocket := UDPSocketNew();
    var daddr: int64 := BufferAlloc(SOCKADDR_IN_SIZE);
    SockAddrInCreate(daddr, ip, 19090);
    var sent: int64 := UDPSocketSendTo(tx, "MESSWERT=23"c as int64, 11, daddr as pchar);
    PrintLn("gesendet: ", IntToStr(sent), " Bytes");

    var buf: int64 := BufferAlloc(64);
    var src: int64 := BufferAlloc(SOCKADDR_IN_SIZE);
    var alen: int64 := BufferAlloc(8);
    var n: int64 := UDPSocketRecvFrom(rx, buf, 64, src, alen);
    PrintLn("empfangen: ", IntToStr(n), " Bytes -> ", buf as pchar);

    UDPSocketClose(tx);
    UDPSocketClose(rx);
    return 0;
}

Ausgabe:

Empfaenger gebunden an 127.0.0.1:19090
gesendet: 11 Bytes
empfangen: 11 Bytes -> MESSWERT=23

UDPSocketRecvFrom schreibt die Absenderadresse in src und deren Länge in alen; beide Puffer müssen bereitstehen, auch wenn man den Absender nicht auswertet.

DNS-Auflösung

import std.net.dns;

fn main(): int64 {
    var ip: int64 := GetHostByName("example.com");
    Print("example.com -> ");
    DNSPrintIP(ip);
    PrintLn("");
    PrintLn("roher Wert = ", IntToStr(ip));
    return 0;
}

Ausgabe:

example.com -> 172.66.147.243
roher Wert = 2890044403

GetHostByName ist der einfache Weg und liefert eine gepackte IPv4-Adresse als int64. Für Record-Typen jenseits von A — MX, TXT, SRV, CAA … — gibt es DNSResolveMX und Verwandte; die nehmen alle vier Parameter (hostname, hostnameLen, dnsServerIP, resultPtr) und schreiben ihr Ergebnis in einen übergebenen Puffer.

HTTP-Anfrage

import std.net.http;

fn main(): int64 {
    var resp: HTTPResponse := HTTPGet("example.com"c, "/"c);
    PrintLn("Status        = ", IntToStr(resp.statusCode));
    PrintLn("Content-Length= ", IntToStr(resp.contentLength));
    PrintLn("Body-Bytes    = ", IntToStr(resp.bodySize));
    PrintLn("Header-Anzahl = ", IntToStr(resp.headerCount));
    HTTPResponseFree(resp);
    PrintLn("freigegeben");
    return 0;
}

Ausgabe:

Status        = 200
Content-Length= 0
Body-Bytes    = 571
Header-Anzahl = 0
freigegeben

Der Antwortkörper steht als Zeiger in resp.bodyPtr mit Länge resp.bodySizenicht in einem Feld body. contentLength bleibt hier 0, weil die Gegenstelle Transfer-Encoding: chunked verwendet; bodySize ist der verlässliche Wert. headerCount ist immer 0 (#1453) — die Rohheader stehen stattdessen in resp.headersRaw.

Für HTTPS dieselbe Struktur mit import std.net.https und HTTPSGet(host, path) — ebenfalls zwei Parameter, ohne Port.

epoll — auf mehrere Verbindungen warten

import std.net.epoll;
import std.net.socket;
import std.net.types;

fn main(): int64 {
    var ip: int64 := IPPack(127, 0, 0, 1);
    var srv: TCPListener := TCPListenerNew();
    var addr: int64 := BufferAlloc(SOCKADDR_IN_SIZE);
    SockAddrInCreate(addr, ip, 19191);
    TCPListenerBind(srv, addr);
    TCPListenerListen(srv, 16);

    var ep: int64 := EpollCreate();
    PrintLn("epoll-fd = ", IntToStr(ep));
    EpollAdd(ep, srv.fd, EPOLLIN, srv.fd);

    var events: int64 := BufferAlloc(EPOLL_EVENT_SIZE * 16);
    PrintLn("EpollWait ohne Verbindung (Timeout 100 ms): ", IntToStr(EpollWait(ep, events, 16, 100)));

    var cli: TCPConn := TCPConnect(ip, 19191);
    var n: int64 := EpollWait(ep, events, 16, 1000);
    PrintLn("EpollWait nach connect: ", IntToStr(n), " Ereignis(se)");
    if (n > 0) {
        PrintLn("  fd    = ", IntToStr(EpollEventFd(events, 0)));
        PrintLn("  flags = ", IntToStr(EpollEventFlags(events, 0)), " (EPOLLIN = ", IntToStr(EPOLLIN), ")");
    }
    TCPConnClose(cli);
    TCPListenerClose(srv);
    return 0;
}

Ausgabe:

epoll-fd = 4
EpollWait ohne Verbindung (Timeout 100 ms): 0
EpollWait nach connect: 1 Ereignis(se)
  fd    = 3
  flags = 1 (EPOLLIN = 1)

EpollAdd nimmt vier Argumente: (epFd, fd, events, data). Das data-Feld wird bei jedem Ereignis zurückgegeben und ist der übliche Weg, ein Ereignis seinem Kontext zuzuordnen — hier einfach der fd selbst. Der Ereignispuffer wird mit EPOLL_EVENT_SIZE * maxEvents Bytes bemessen und mit EpollEventFd/EpollEventFlags/EpollEventData ausgelesen.


Bekannte Fehler

Geprüft mit lyxc 1.0.21A.

HTTP-Kopfzeilen (behoben, #1452)

HTTPGetHeader liefert seit 1.0.20F nur noch den Wert der gesuchten Zeile, und headerCount wird gesetzt. Einzelheiten samt Messung auf der Seite zu std.net.http — dort steht auch der Unterschied zwischen contentLength und bodySize bei Transfer-Encoding: chunked.

====

var srv: int64 := HTTPGetHeader(resp, "Server"c);
PrintLn("Server = ", srv as pchar);

Server = cloudflare
Last-Modified: Wed, 12 Aug 2026 20:17:18 GMT
Allow: GET, HEAD
Accept-Ranges: bytes
…

Der Rückgabewert zeigt in den Rohheaderblock, wird aber am Zeilenende nicht abgeschnitten. Wer den Wert weiterverarbeitet, bekommt alle folgenden Header dazu. Bis zum Fix selbst am ersten \r oder \n abschneiden.

HTTPResponse.headerCount bleibt 0 (#1453)

Das Feld wird nie befüllt, auch wenn die Antwort ein Dutzend Header enthält. Wer über Header iterieren will, muss resp.headersRaw (resp.headersSize Bytes, nullterminiert) selbst zerlegen.

Veraltete Funktionsnamen in älteren Beispielen

Frühere Fassungen dieser Seite und einiger Unterseiten zeigten Namen, die es in der Unit nie gab: SocketTCPListen, SocketAccept, SocketRead, SocketWrite, SocketClose, DNSResolveA. Die tatsächlichen Namen sind TCPListenerNew/Bind/Listen, TCPConnRead/Write/Close und GetHostByName bzw. DNSResolve. Wer solchen Code vorfindet, hat ihn nie übersetzt.


Verwandte Units