← Startseite
🔌

Vorlesung 9

Netzwerkprogrammierung mit Sockets
Wie zwei Programme ĂŒber das Netz miteinander reden — TCP & UDP in C

Verteilte Systeme
Prof. Dr. Alexandra Mikityuk
HTW Berlin

Sockets TCP UDP

Lernziele

  • Verstehen, was ein Socket ist — der Endpunkt jeder Netzwerk-Kommunikation
  • IP-Adresse + Port als vollstĂ€ndige Adresse eines Programms im Netz lesen
  • Einen TCP-Server und -Client in C schreiben (socket · bind · listen · accept · connect · send/recv)
  • TCP vs. UDP sicher unterscheiden — und wissen, wann man welches nimmt
  • Verstehen, warum Byte-Order (htons) zwischen Rechnern wichtig ist

RĂŒckblick: bisher alles auf einem Rechner

In VL 8 liefen mehrere Prozesse/Threads nebeneinander — aber alle auf derselben Maschine, mit gemeinsamem Speicher.

đŸ§” VL 8 — ein Rechner

Threads teilen Speicher → Synchronisation mit Mutex/Semaphor. Kommunikation = gemeinsame Variablen.

🌍 Verteiltes System — viele Rechner

Kein gemeinsamer Speicher! Die Prozesse stehen auf verschiedenen Maschinen. Wie reden sie jetzt?

Die Antwort heißt: Nachrichten ĂŒber das Netz austauschen. Das Werkzeug dafĂŒr — die Grundlage von HTTP (VL 6), MQTT (VL 7) und dem Bitcoin-P2P-Netz (VL 4/5) — ist der Socket.

Wie reden zwei Prozesse ĂŒberhaupt? — IPC

Interprozesskommunikation (IPC) — abhĂ€ngig davon, wo die Prozesse stehen:

🏠 Gleicher Rechner

  • Shared Memory — gemeinsamer Speicher
  • Pipes — ein Datenstrom (| in der Shell)
  • Signale — kurze „Klopfzeichen" (z. B. SIGTERM)

🌐 Über das Netz

  • Sockets — funktionieren auf einem Rechner und ĂŒber das Internet
  • Das universelle Werkzeug fĂŒr verteilte Systeme
Warum Sockets? Dieselbe API funktioniert lokal und weltweit — euer Code Ă€ndert sich kaum, egal ob der Partner-Prozess nebenan oder in einem Rechenzentrum in den USA lĂ€uft.

Das Grundbild: Client fragt, Server antwortet

Fast jede Netz-Kommunikation folgt diesem Muster — wie im Restaurant: ihr bestellt, die KĂŒche liefert.

đŸ’» Client đŸ–„ïž Server Anfrage: „gib mir X" Antwort: „hier ist X"
Merke: Der Server lĂ€uft stĂ€ndig und wartet auf Anfragen. Der Client ergreift die Initiative und fragt. Beide reden ĂŒber je einen Socket.

Was ist ein Socket?

Ein Socket ist der Endpunkt einer Verbindung — eine „Steckdose", in die euer Programm Daten hineinschreibt und herausliest.

đŸ“€ Programm A
→
🔌 Socket
— Netz —
🔌 Socket
→
đŸ“„ Programm B

Der Clou: In C fĂŒhlt sich ein Socket an wie eine Datei. Man bekommt einen int (den „File Descriptor") und nutzt vertraute Operationen — nur heißen sie send() / recv() statt write() / read().

Merksatz: „Everything is a file" — auch das Netz. Ein Socket ist ein Daten-Endpunkt, den man wie eine Datei beschreibt und liest.

Die Adresse: IP + Port

Damit eine Nachricht das richtige Programm erreicht, braucht es zwei Angaben:

🏱 IP-Adresse

Welcher Rechner? — wie die Hausanschrift. Z. B. 127.0.0.1 (= „dieser Rechner", localhost).

đŸšȘ Port

Welches Programm darauf? — wie die Wohnungsnummer. Z. B. 80 (HTTP), 443 (HTTPS), 8080 (eigene App).

127.0.0.1 : 8080

Rechner     :     Programm

Ports 0–65535: 0–1023 sind „well-known" (fĂŒr Systemdienste, brauchen Root). Eure eigenen Programme nehmen typischerweise Ports ab 1024 (z. B. 8080).

Ein Rechner, viele TĂŒren — die Ports

Die IP fĂŒhrt zum richtigen Rechner. Aber dort laufen viele Programme gleichzeitig — der Port sagt, welches gemeint ist.

🌍 ein Rechner (IP 203.0.113.5) :80 Web-Server :25 Mail-Server :8080 eure App
Bild: Die IP ist das GebĂ€ude, die Ports sind die TĂŒren. Eine Anfrage an 203.0.113.5:8080 landet genau bei „eurer App" — nicht beim Web- oder Mail-Server.

Zwei Transport-Wege: TCP & UDP

Auf Sockets gibt es zwei Grund-Protokolle — mit komplett unterschiedlicher Philosophie:

📞 TCP — der Telefonanruf

Erst Verbindung aufbauen, dann reden. ZuverlÀssig & geordnet: nichts geht verloren, alles kommt in Reihenfolge an.

SOCK_STREAM

📼 UDP — die Postkarte

Keine Verbindung — einfach abschicken. Schnell, aber ohne Garantie: Pakete können verloren gehen oder die Reihenfolge tauschen.

SOCK_DGRAM

Der einzige Code-Unterschied am Anfang: socket(AF_INET, SOCK_STREAM, 0) fĂŒr TCP vs. SOCK_DGRAM fĂŒr UDP. Den Rest schauen wir uns gleich konkret an.

TCP — der Ablauf in Socket-Aufrufen

Server und Client durchlaufen feste Schritte. Der Server wartet, der Client verbindet sich.

đŸ–„ïž Server

socket()
↓
bind() — Port belegen
↓
listen() — lauschen
↓
accept() — wartet auf Client
↓
recv() / send()

đŸ’» Client

socket()
↓
connect() — zum Server
↓
send() / recv()
Client: connect() đŸ€ Server: accept() → ✓ Verbindung steht
Hier treffen sie sich: Der Client-Aufruf connect() und der Server-Aufruf accept() finden zusammen — das ist der 3-Wege-Handshake (gleich im Detail). Danach steht eine zuverlĂ€ssige Leitung.

Wie eine TCP-Verbindung entsteht: der 3-Wege-Handshake

Bevor auch nur ein Byte Nutzdaten fließt, begrĂŒĂŸen sich Client und Server in drei Schritten — wie ein HĂ€ndedruck:

Client Server 1. SYN — „lass uns reden" 2. SYN-ACK — „ok, ich bin bereit" 3. ACK — „los geht's" ✓ Verbindung steht — jetzt fließen Daten
Genau das passiert beim Treffen von connect() (Client) und accept() (Server). Erst danach gibt es eine zuverlĂ€ssige Leitung — der Preis fĂŒr TCPs Garantie.

TCP-Server in C

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main(void) {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);   // 1. Socket erstellen

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(8080);          // Port (Byte-Order!)
    addr.sin_addr.s_addr = INADDR_ANY;        // alle Netzwerk-Interfaces

    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); // 2. Port belegen
    listen(server_fd, 5);                                  // 3. Lauschen

    int client = accept(server_fd, NULL, NULL);  // 4. Auf Client warten

    char buf[1024];
    int n = recv(client, buf, sizeof(buf)-1, 0); // 5. schreibt Client-Bytes IN buf
    buf[n] = '\0';
    printf("Empfangen: %s\n", buf);
    send(client, buf, n, 0);                  // 6. Echo: dieselben Bytes aus buf zurĂŒck

    close(client);  close(server_fd);
    return 0;
}
Wo kommt „Hallo!" her? buf ist nur eine leere Box. recv fĂŒllt sie mit dem, was der Client schickt — danach steht „Hallo!" in buf. send(client, buf, n, 
) schickt genau diese Bytes zurĂŒck. Nichts ist fest verdrahtet: zurĂŒck kommt, was reinkam → „Echo".
Kompilieren & starten: gcc server.c -o server · ./server — es wartet jetzt auf Port 8080.

TCP-Client in C

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main(void) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);      // 1. Socket

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);   // Server-IP

    connect(sock, (struct sockaddr*)&addr, sizeof(addr)); // 2. Verbinden

    char *msg = "Hallo Server!";
    send(sock, msg, strlen(msg), 0);             // 3. Senden

    char buf[1024];
    int n = recv(sock, buf, sizeof(buf)-1, 0);  // 4. Antwort empfangen
    buf[n] = '\0';
    printf("Server antwortete: %s\n", buf);

    close(sock);
    return 0;
}
Ausgabe: Server zeigt „Empfangen: Hallo Server!", Client zeigt „Server antwortete: Hallo Server!". Die Nachricht ist hin und zurĂŒck gereist. ✅

Server + Client im Zusammenspiel — der ganze Ablauf

So greifen die beiden Programme von vorhin ineinander. Lies von oben nach unten:

Client Server socket·bind·listen·accept ⏳ socket · connect Verbindung (accept trifft connect) send("Hallo Server!") → recv() send(echo) → recv() ✓ Nachricht hin und zurĂŒck
Das ist „Echo": Der Client schickt einen Text, der Server schickt genau denselben zurĂŒck. Klein — aber es ist eine vollstĂ€ndige, echte Netzwerk-Kommunikation.

Warum htons()? — die Byte-Reihenfolge

Verschiedene CPUs speichern Zahlen in unterschiedlicher Byte-Reihenfolge. Über das Netz muss man sich auf eine einigen.

🔀 Das Problem

Die Zahl 8080 kann als 1F 90 (Big-Endian) oder 90 1F (Little-Endian) im Speicher liegen — je nach CPU. Schickt man die rohen Bytes, liest der andere evtl. Unsinn.

✅ Die Lösung: Network Byte Order

Im Netz gilt Big-Endian. Funktionen wandeln um:

Lest den Namen einfach Buchstabe fĂŒr Buchstabe:

  • htons = host to network short  (16 Bit, z. B. Port)
  • htonl = host to network long  (32 Bit, z. B. IPv4-Adresse)
  • ntohs / ntohl = network to host (zurĂŒck) — short / long

Warum beide? Daten reisen in beide Richtungen: beim Senden wandelt ihr eure Zahl in Netz-Reihenfolge (hton
), beim Empfangen wieder zurĂŒck in eure (ntoh
).

Die Zahl 8080 (Port) im Speicher: Netz (Big-Endian): 1F 90 manche CPU (Little-Endian): 90 1F
Faustregel: Ports und IP-Zahlen immer mit htons()/htonl() setzen. Vergisst man es, lĂ€uft es auf dem eigenen Rechner vielleicht — aber bricht zwischen zwei verschiedenen Maschinen.

Kurzer Zwischenstand — wo stehen wir?

Bevor wir UDP anschauen — das habt ihr bis hier in der Hand:

🔌 Socket & Adresse

Endpunkt wie eine Datei. Adresse = IP (Rechner) + Port (Programm).

📞 TCP-Verbindung

Server: socket→bind→listen→accept. Client: socket→connect. Dann send/recv.

🔱 Byte-Order

Zahlen fĂŒrs Netz mit htons/htonl — sonst Chaos zwischen CPUs.

Alles bisher war TCP — der zuverlĂ€ssige Weg. Jetzt der schnelle, einfachere: UDP.

UDP in C — verbindungslos, kĂŒrzer

UDP ist verbindungslos: man verschickt einzelne Pakete (Datagramme) — ganz ohne vorher eine Leitung aufzubauen. Darum ist der Code kĂŒrzer.

đŸ“„ Server

int s = socket(AF_INET, SOCK_DGRAM, 0);
// addr fĂŒllen (Port 9090, INADDR_ANY)
bind(s, (struct sockaddr*)&addr, sizeof(addr));

struct sockaddr_in cli;
socklen_t len = sizeof(cli);
char buf[1024];
int n = recvfrom(s, buf, 1023, 0,
       (struct sockaddr*)&cli, &len);
sendto(s, buf, n, 0,
       (struct sockaddr*)&cli, len); // Echo

đŸ“€ Client

int s = socket(AF_INET, SOCK_DGRAM, 0);
// addr fĂŒllen (127.0.0.1, Port 9090)

char *msg = "Hallo UDP!";
sendto(s, msg, strlen(msg), 0,
       (struct sockaddr*)&addr, sizeof(addr));

char buf[1024];
int n = recvfrom(s, buf, 1023, 0,
       NULL, NULL);
Die drei Kern-Ideen:
  • Keine Verbindung → kein listen/accept/connect. Man schickt sofort los.
  • Adresse bei jedem Paket: sendto sagt „wohin", recvfrom verrĂ€t „von wem". Deshalb merkt sich der Server mit &cli den Absender und antwortet genau dorthin.
  • Jedes sendto = ein eigenstĂ€ndiges Paket (Datagramm) — unabhĂ€ngig, ohne Reihenfolge-Garantie.
Der Preis: ohne Verbindung kann ein Paket einfach verloren gehen — und niemand merkt es. Genau deshalb ist UDP schnell, aber unzuverlĂ€ssig.

Warum „zuverlĂ€ssig" vs. „schnell"? — die Pakete

📞 TCP — zĂ€hlt & bestĂ€tigt

1 ✓ 2 ✗ 3 ✓ 2 â†ș

Jedes Paket ist nummeriert und wird bestĂ€tigt (ACK). Fehlt eins, wird es neu geschickt → nichts geht verloren, Reihenfolge stimmt.

📼 UDP — abschicken & vergessen

1 2 ✗ 3

Pakete werden einfach gesendet — keine BestĂ€tigung, keine Wiederholung. Geht eins verloren, merkt es niemand. DafĂŒr ganz ohne Wartezeit.

Das ist der ganze Unterschied: TCPs Garantie kostet Zeit (BestĂ€tigen & Nachsenden). UDP spart genau diese Zeit — und nimmt dafĂŒr Verluste in Kauf.

TCP vs. UDP — wann was?

TCP 📞UDP 📼
Verbindungja (Handshake)nein
ZuverlĂ€ssig✅ nichts geht verloren❌ Pakete können fehlen
Reihenfolgegarantiertnicht garantiert
Tempo / Overheadetwas langsamersehr schnell
Gut fĂŒrWeb, E-Mail, Dateien, DatenbankenLive-Video, Online-Spiele, DNS, Voice
Faustregel: MĂŒssen wirklich alle Daten korrekt ankommen? → TCP. ZĂ€hlt jede Millisekunde und ein verlorenes Paket ist egal? → UDP. (Ein ruckelndes Videobild ist besser als eines, das auf ein neu gesendetes Paket wartet.)

Was reist eigentlich durch den Socket?

Ein Socket transportiert nur rohe Bytes — er weiß nichts ĂŒber deren Bedeutung. Sender und EmpfĂ€nger mĂŒssen sich auf ein Format einigen.

📩 Ein vereinbartes Format — z. B. JSON

{ "typ": "messung", "sensor": 3, "wert": 21.5 }

Der Server schickt diesen Text durch den Socket; der Client liest die Bytes und parst sie zurĂŒck in Werte.

Genau das macht ein Protokoll: Regeln, wie die Bytes aufgebaut sind. HTTP (VL 6) und MQTT (VL 7) sind solche Protokolle — und beide laufen unten auf TCP-Sockets.

Sockets sind das Fundament — alles sitzt darauf

Was ihr in diesem Kurs schon gesehen habt, lĂ€uft unten ĂŒber Sockets:

🌐 HTTP / REST (VL 6)

Browser ↔ Server ĂŒber einen TCP-Socket auf Port 80/443.

📡 MQTT (VL 7)

Clients ↔ Broker ĂŒber TCP-Sockets — Publish/Subscribe obendrauf.

⛓ Bitcoin-P2P (VL 4/5)

Nodes verbinden sich gegenseitig ĂŒber Sockets und tauschen Blöcke aus.

Die große Einordnung: Ein Socket ist die unterste, gemeinsame Schicht jeder Netzwerk-Anwendung. Wer Sockets versteht, versteht, wie verteilte Systeme im Kern wirklich kommunizieren.

🔧 Selbst ausprobieren — in 30 Sekunden

Ihr braucht nicht mal den Client zu schreiben: startet den Echo-Server von vorhin und verbindet euch mit netcat (nc), das auf fast jedem System dabei ist.

đŸ–„ïž Terminal 1 — Server

$ gcc server.c -o server
$ ./server
# wartet auf Port 8080 ...
Empfangen: Hallo!

đŸ’» Terminal 2 — Client (netcat)

$ nc 127.0.0.1 8080
Hallo!          ← ihr tippt
Hallo!          ← Echo vom Server
Warum kommt „Hallo!" zurĂŒck? Das ist der Echo-Server: er druckt das Empfangene (printf → Terminal 1) und schickt dieselben Bytes zurĂŒck (die send(... buf ...) // Echo-Zeile aus dem Server-Code) → Terminal 2 zeigt es an.
Tipp: Mit nc testet ihr jeden TCP-Server, ohne Client-Code. Genau so prĂŒft man auch echte Dienste: „lauscht da was und antwortet es?"

🧠 Quiz

Ihr baut ein Online-Spiel: viele Positions-Updates pro Sekunde, ein einzelnes verlorenes Update ist egal. Welches Protokoll passt?

ATCP — weil es zuverlĂ€ssig ist
BUDP — schnell, verlorene Pakete sind hier verkraftbar
CHTTP — weil es im Browser lĂ€uft
DEgal, beide sind identisch

→ Klicke auf eine Antwort, um aufzulösen

Zusammenfassung

  • Socket = Endpunkt der Netzwerk-Kommunikation; in C wie eine Datei (send/recv).
  • Adresse = IP (welcher Rechner) + Port (welches Programm).
  • TCP-Server: socket → bind → listen → accept → recv/send. Client: socket → connect → send/recv.
  • UDP: verbindungslos, sendto/recvfrom — schnell, aber ohne Garantie.
  • TCP = zuverlĂ€ssig & geordnet · UDP = schnell & verlustbehaftet.
  • htons/htonl fĂŒr die Byte-Order — sonst bricht es zwischen verschiedenen Rechnern.
  • Sockets sind das Fundament von HTTP, MQTT und P2P.

📚 Quellen & zum Ausprobieren

📖 Lesen

  • Beej's Guide to Network Programming — der Klassiker zu Sockets in C (kostenlos online)
  • Kerrisk, The Linux Programming Interface — Kapitel Sockets
  • man socket, man 7 ip, man recv

🔧 Selbst testen

  • Server starten, dann mit nc 127.0.0.1 8080 (netcat) verbinden
  • ss -tlnp / netstat — welche Ports lauschen?
  • Wireshark: TCP-Handshake live mitsehen
Übung: Erweitert den Echo-Server so, dass er in einer Schleife mehrere Clients nacheinander bedient — und denkt daran, was ihr aus VL 8 ĂŒber mehrere gleichzeitige Verbindungen (Threads!) wisst.

Fragen?

NĂ€chstes Mal bauen wir auf den Sockets auf — von der rohen Verbindung zur echten verteilten Anwendung.

Prof. Dr. Alexandra Mikityuk

HTW Berlin · Verteilte Systeme

© 2026 HTW Berlin · Verteilte Systeme

1 /