TCP ist ein verbindungsorientiertes Übertragungsprotokoll und die Grundlage fast aller Dienste im Internet. Wer versteht, wie der Verbindungsaufbau funktioniert, kann Netzwerkprobleme deutlich schneller eingrenzen.

Der Aufbau: Drei Nachrichten

Client                                 Server
  |  --- SYN (seq=x) -------------------->  |   "ich moechte verbinden"
  |  <-- SYN-ACK (seq=y, ack=x+1) --------  |   "einverstanden"
  |  --- ACK (ack=y+1) ------------------>  |   "bestaetigt"
  |                                          |
  |  ========== Datenfluss ===============   |

Das ist der "Three-Way Handshake". Jede der drei Nachrichten
hat eine Aufgabe:
  SYN       Client nennt eine Startnummer (seq) fuer seine Bytes
  SYN-ACK   Server nennt die eigene Startnummer und bestaetigt
  ACK       Client bestaetigt die Startnummer des Servers

Erst danach fliessen Nutzdaten. Das kostet eine Verzoegerung
(Round-Trip) - der Grund, warum HTTP/3 (QUIC) diesen Schritt
mit dem Verbindungsaufbau von TLS zusammenlegt.

Der Abbau: Vier Nachrichten

  |  --- FIN -------------------------->  |   "ich habe nichts mehr"
  |  <-- ACK ---------------------------  |   "verstanden"
  |  <-- FIN ---------------------------  |   "ich auch nicht mehr"
  |  --- ACK -------------------------->  |   "verstanden"
  |                                         |

Zustaende, die man im Werkzeug "ss" sieht:
  LISTEN        wartet auf Verbindungen
  SYN-SENT      Client hat SYN geschickt, wartet auf SYN-ACK
  SYN-RECEIVED  Server hat SYN-ACK geschickt
  ESTABLISHED   Verbindung steht
  FIN-WAIT-1/2  eigene Seite hat geschlossen
  CLOSE-WAIT    Gegenstelle hat geschlossen, Anwendung noch nicht
  TIME-WAIT     Wartezeit nach dem Schliessen (2 x MSL, meist 60s)
  LAST-ACK      wartet auf die letzte Bestaetigung

TIME-WAIT ist wichtig: Es verhindert, dass verspaetete Pakete
einer alten Verbindung in eine neue mit gleicher Nummer geraten.
Viele TIME-WAIT-Zustaende auf einem Server sind daher normal -
solange es nicht zehntausende sind.

Verbindungen ansehen

# LISTENDE Sockets mit Prozess
ss -tlnp

# ALLE TCP-Verbindungen mit Prozess
ss -tanp | head -40

# Nur bestehende Verbindungen
ss -tn state established

# Anzahl je Zustand - der schnellste Ueberblick bei Last
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn

# Wer verbindet sich oft mit Port 443?
ss -tn dst :443 | awk 'NR>1 {print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head

# Netzwerkstatistik seit dem Start
ss -s
nstat -az | grep -E 'TcpRetrans|TcpExtTCPAbortOn|TcpOutSegs|TcpAttemptFails'

# Das wichtigste Leistungsmass: Wiederholungen
#   TcpRetransSegs / TcpOutSegs  -> sollte unter 0,5 % liegen
awk '/TcpRetransSegs|TcpOutSegs/ {print $1, $2}' /proc/net/snmp

# Zeitgeber des Kernels
sysctl net.ipv4.tcp_keepalive_time          # 7200 s - recht lang
sysctl net.ipv4.tcp_fin_timeout             # 60 s
sysctl net.ipv4.tcp_tw_reuse                # TIME-WAIT wiederverwenden

# Fehlersuche: Wer lauscht auf einem Port?
ss -tlnp 'sport = :8080'
lsof -i :8080
fuser -n tcp 8080

Warum das bei der Fehlersuche hilft

"Verbindung wird abgelehnt" (Connection refused)
   -> SYN erreicht den Server, aber es lauscht nichts.
   -> Kein Paketfilter-Problem: die Anwendung laeuft nicht
      oder lauscht auf einer anderen Adresse.
   -> ss -tlnp zeigt, worauf sie lauscht (0.0.0.0 oder 127.0.0.1!)

"Verbindung laeuft in die Zeitueberschreitung" (timeout)
   -> SYN wird verworfen. Typisch: Paketfilter, Firewall,
      falsche Route, falscher Port in der Weiterleitung.
   -> Von aussen pruefen: tcpdump auf dem Server
      (kommen die SYN-Pakete ueberhaupt an?)

"Verbindung bricht nach 60 Sekunden zusammen"
   -> Mittendrin keine Pakete mehr: NAT-Tabelle abgelaufen
      (TCP-Zeitueberschreitung) oder ein Paketfilter mit
      Zustandsverfolgung hat den Eintrag verloren.

"Verbindung bleibt bei TLS stehen"
   -> TCP funktioniert, aber der Handshake scheitert:
      Zertifikat, Protokollversion, Zwischenstelle (Proxy)
      oder ein falscher Aufrufname (SNI).

"Manche Verbindungen klappen, andere nicht"
   -> Lastverteilung: eine von mehreren Gegenstellen ist
      defekt oder hat ein anderes Zertifikat. Von Hand
      jede Adresse einzeln ansprechen.

"Zeitueberschreitung nur bei grossen Uebertragungen"
   -> MSS/MTU-Problem: Fragmente werden unterwegs verworfen.
      Test: ping -M do -s 1472  (1472+28 = 1500)

Sockets und Verbindungen unter Linux

# Verbindungsanfragen in der Warteschlange (Backlog)
ss -tln | awk 'NR>1 {print $2, $3, $4, $5}'
#   Recv-Q bei LISTEN = wartende Verbindungen
#   Send-Q bei LISTEN = Backlog-Grenze
#   Ist Recv-Q dauerhaft hoch -> Anwendung nimmt zu langsam an

# Verbindungswarteschlange des Kernels
sysctl net.core.somaxconn               # 4096 ist ein guter Wert
sysctl net.ipv4.tcp_max_syn_backlog
#   "SYNs to LISTEN sockets dropped" steigend -> Backlog zu klein

# Ports in Benutzung
ss -tln | wc -l
ss -tan | wc -l

# Zeitueberwachung: TCP-Verbindungen, die nie fertig werden
sysctl net.ipv4.tcp_syn_retries         # 6 (etwa 127 s)
sysctl net.ipv4.tcp_synack_retries

# Wichtig fuer Server hinter Lastverteilern: Die echte Quelle
#   TCP kennt keine Weiterleitungs-Kopfzeilen. Adressen
#   kommen ueber X-Forwarded-For (HTTP) oder PROXY protocol
#   (TCP). Ohne das sieht die Anwendung nur den Lastverteiler.

Prüfliste

  1. Kennst du den Unterschied zwischen Connection refused und Timeout – und was er jeweils bedeutet?
  2. Ist auf dem Server geprüft, ob die Anwendung auf 0.0.0.0 oder nur auf 127.0.0.1 lauscht?
  3. Ist die Wiederholungsrate des Netzes im Blick (Zielwert unter 0,5 %)?
  4. Ist somaxconn für stark belastete Dienste angepasst?
  5. Sind die Zeitlimits von Lastverteiler und Anwendung aufeinander abgestimmt (kein 30-s-Proxy vor einer 120-s-Abfrage)?
  6. Ist die Weiterleitung der echten Client-Adresse gesetzt?
  7. Ist die MTU auf dem gesamten Pfad einheitlich?

Häufige Fehler

  1. Anwendung lauscht nur lokal. Der Klassiker: ss -tlnp zeigt 127.0.0.1:8080, von außen kommt nichts an. Die Anwendung muss auf 0.0.0.0 lauschen.
  2. Firewall-Regel vergessen. Der Dienst läuft, aber Pakete werden verworfen. Timeout statt Fehlermeldung.
  3. Zu langes tcp_keepalive_time. Zwei Stunden sind für viele Anwendungen zu lang. Lastverteiler und Firewalls werfen die Verbindung vorher aus ihrer Tabelle – der Client merkt es erst beim nächsten Schreibversuch.
  4. Ports doppelt belegt. Zwei Prozesse auf demselben Port: Der zweite startet nicht. SO_REUSEPORT glaubt zu „funktionieren“, verteilt aber zufällig.
  5. Verbindungsabbruch nach einer Minute. Meist eine NAT-Tabelle, die abschaltet. Mit Keepalive-Paketen in kürzeren Abständen oder tcp_keepalive_time kleiner als die NAT-Zeitüberschreitung lösen.
  6. MSS-Klemmen (MSS clamping) fehlt. Bei einer VPN- oder Tunnelstrecke werden große Pakete verworfen, kleine gehen durch. Von außen sichtbar als „Seite lädt, aber Bilder fehlen“.

Verwandte Grundlagen: UDP, IP-Adresse, tcpdump, ss und netstat.