====== std.net — Netzwerk ====== → [[lyx_-_programmiersprache:units|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/'' **''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. ===== Unit-Übersicht ===== ^ Unit ^ Gruppe ^ Beschreibung ^ | [[lyx_-_programmiersprache:units:net:socket|std.net.socket]] | Transport | TCP, UDP, Unix-Domain, Raw-Sockets, ICMP, ARP, poll/select, IPv6 | | [[lyx_-_programmiersprache:units:net:types|std.net.types]] | Transport | Adressstrukturen (''SockAddrInCreate''), IP-Parsing, Byte-Order, IPv6-Helfer | | [[lyx_-_programmiersprache:units:net:tls|std.net.tls]] | Transport | TLS/SSL über OpenSSL | | [[lyx_-_programmiersprache:units:net:syscalls|std.net.syscalls]] | Transport | Portable POSIX-Socket-Syscalls (unterste Schicht) | | [[lyx_-_programmiersprache:units:net:epoll|std.net.epoll]] | Transport | epoll-Multiplexing: ''EpollCreate'', ''EpollAdd''/''Mod''/''Del'', ''EpollWait'', ''EventFd*'' | | [[lyx_-_programmiersprache:units:net:dns|std.net.dns]] | Protokolle | DNS-Auflösung (A, AAAA, MX, NS, TXT, SOA, SRV, CAA, DNSKEY, DS, PTR), DoH, Cache | | [[lyx_-_programmiersprache:units:net:http|std.net.http]] | Protokolle | HTTP/1.1-Client: GET, POST, PUT, DELETE, PATCH, HEAD, Redirects, Auth-Header | | [[lyx_-_programmiersprache:units:net:https|std.net.https]] | Protokolle | HTTPS-Client (TLS davor) | | [[lyx_-_programmiersprache:units:net:smtp|std.net.smtp]] | Protokolle | SMTP-Mailversand (Ports 25/587/465) | | [[lyx_-_programmiersprache:units:net:imap|std.net.imap]] | Protokolle | IMAP4rev1-Postfachzugriff: SELECT, LIST, FETCH | | [[lyx_-_programmiersprache:units:net:ssh|std.net.ssh]] | Protokolle | SSH: Auth, Remote-Befehle, Output-Capture (libssh2) | | [[lyx_-_programmiersprache:units:net:mqtt|std.net.mqtt]] | Protokolle | MQTT 3.1.1: Publish, Subscribe, QoS 0/1/2 | | [[lyx_-_programmiersprache:units:net:ntp|std.net.ntp]] | Protokolle | NTP v3: Zeitabfrage via UDP Port 123 | | [[lyx_-_programmiersprache:units:net:dhcp|std.net.dhcp]] | Protokolle | DHCP-Client (RFC 2131): DORA-Lease, Paket-Builder, Reply-Parser | | [[lyx_-_programmiersprache:units:net:arp|std.net.arp]] | Protokolle | ARP (RFC 826): Request/Reply, Frame-Parser, Kernel-Cache via SIOCGARP | | [[lyx_-_programmiersprache:units:net:frames|std.net.eth / ipv4 / udp]] | Protokolle | Reine Frame-Builder ohne Syscalls und ohne Allokation — kernel-tauglich | | [[lyx_-_programmiersprache:units:net:quic|std.net.quic]] | Protokolle | QUIC (RFC 9000): UDP-basiert, TLS 1.3 integriert | | [[lyx_-_programmiersprache:units:net:sip|std.net.sip]] | Protokolle | SIP (RFC 3261): REGISTER, INVITE, OPTIONS | | [[lyx_-_programmiersprache:units:net:telnet|std.net.telnet]] | Protokolle | Telnet (RFC 854): Option-Negotiation | | [[lyx_-_programmiersprache:units:net:whois|std.net.whois]] | Protokolle | WHOIS (RFC 3912): Auto-Routing zur Registry | | [[lyx_-_programmiersprache:units:net:ldap|std.net.ldap]] | Verzeichnis & Routing | LDAP (RFC 4511): Bind, Search, Modify, SASL | | [[lyx_-_programmiersprache:units:net:bgp|std.net.bgp]] | Verzeichnis & Routing | BGP-4 (RFC 4271): AS-Routing, UPDATE-Messages | | [[lyx_-_programmiersprache:units:net:snmp|std.net.snmp]] | Management | SNMPv1/v2c: GET, SET, WALK über OIDs | | [[lyx_-_programmiersprache:units:net:asn1|std.net.asn1]] | Management | ASN.1/BER-Kodierung (Basis für SNMP) | | [[lyx_-_programmiersprache:units:net:mongo|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 [[https://github.com/SEOLizer/LyX-Compiler/issues/1615|#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 [[lyx_-_programmiersprache:units:net:http|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 ===== * [[lyx_-_programmiersprache:units:url|std.url]] — URL-Parsing für HTTP-Anfragen * [[lyx_-_programmiersprache:units:db:mysql|std.db.mysql]] · [[lyx_-_programmiersprache:units:db:redis|std.db.redis]] — Datenbankprotokolle * [[lyx_-_programmiersprache:units:crypto:aes|std.crypto.aes]] — Verschlüsselung für eigene Protokolle * [[lyx_-_programmiersprache:units:io|std.io]] — Datei- und Konsolen-I/O