std.net — Netzwerk
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 deklariert — HTTPGet, 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.bodySize — nicht 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
- std.url — URL-Parsing für HTTP-Anfragen
- std.db.mysql · std.db.redis — Datenbankprotokolle
- std.crypto.aes — Verschlüsselung für eigene Protokolle
- std.io — Datei- und Konsolen-I/O
