HOWTO · Java

Java.Net.SocketTimeoutException: Zeitüberschreitung bei Verbindung

Im heutigen Artikel besprechen wir java.net.SocketTimeoutException: Connection timed out.

Auf dieser Seite

java.net.SocketTimeoutException: Connection timed out bedeutet, dass ein Netzwerkvorgang nicht innerhalb der dafür vorgesehenen Zeit abgeschlossen wurde. Tritt der Fehler beim Öffnen einer Clientverbindung auf, prüfen Sie den Zielhost, den Port, die Route, den Listener und das konfigurierte Zeitlimit. Die Meldung allein zeigt nicht, welcher dieser Punkte verantwortlich ist. Java oder der zugrunde liegende Netzwerk-Stack kann einen Verbindungsfehler auch mit einem anderen Ausnahmetyp oder einer anderen Meldung melden. Untersuchen Sie daher neben dem Meldungstext auch den Vorgang und den Endpunkt.

Was java.net.SocketTimeoutException: Connection timed out bedeutet

Ein TCP-Socket bezeichnet einen Endpunkt einer Verbindung zwischen Anwendungen. Ein Client verwendet java.net.Socket, um eine Verbindung zu einem Server mit java.net.ServerSocket herzustellen. Der Server nimmt die Verbindung an und erhält ein separates Socket für die Kommunikation mit diesem Client. Das Zeitlimit begrenzt, wie lange der jeweilige Vorgang wartet. Es repariert keine Route und macht keinen nicht erreichbaren Dienst verfügbar.

Wenn ein Client Socket.connect(SocketAddress, timeout) aufruft, begrenzt ein positives Zeitlimit in Millisekunden den Verbindungsversuch. Läuft das Zeitlimit ab, bevor die Verbindung zustande kommt, meldet Java eine Zeitüberschreitung. Diese Einstellung unterscheidet sich von einem Socket-Lesezeitlimit: Eine Verbindung kann erfolgreich zustande kommen, während ein späterer Lesevorgang trotzdem zu lange auf Daten wartet.

Verbindungs-Timeouts von Lese- und Accept-Timeouts unterscheiden

Wählen Sie die Einstellung danach, an welcher Stelle der Vorgang wartet:

  • Ein Verbindungs-Timeout gilt, während ein Client eine ausgehende Verbindung herstellt. Verwenden Sie dafür Socket.connect mit einem Zeitlimit.
  • Ein Lese-Timeout gilt, während ein verbundenes Socket auf eingehende Daten wartet. Setzen Sie es mit Socket.setSoTimeout; es begrenzt nicht den Verbindungsaufbau.
  • Ein Accept-Timeout gilt, während ein Server auf einen eingehenden Client wartet. Setzen Sie es mit ServerSocket.setSoTimeout; es begrenzt nicht die ausgehende Verbindung eines Clients.

Diese Zeitlimits sind voneinander unabhängig. Begrenzen Sie jeden Vorgang, der nicht unbegrenzt warten soll, und wählen Sie Werte, die zur erwarteten Netzwerk- und Dienstantwortzeit passen. Bei diesen Socket-APIs bedeutet ein Zeitlimit von null unbegrenztes Warten. Verwenden Sie daher nicht null, wenn Sie ein endliches Zeitlimit benötigen.

Häufige Ursachen und ihre Diagnose

Betrachten Sie mögliche Ursachen als Hypothesen und prüfen Sie sie von dem Rechner und aus dem Netzwerk heraus, in beziehungsweise aus denen der Java-Prozess läuft:

  1. Das Zeitlimit ist zu kurz. Vergleichen Sie es mit der üblichen Verbindungsdauer und dem Zeitbudget der Anwendung. Erhöhen Sie es nur, wenn langsame, aber erfolgreiche Verbindungen zu erwarten sind; mit einem größeren Zeitlimit werden auch Fehler später sichtbar.
  2. Host oder Port sind falsch, oder es lauscht kein Dienst. Prüfen Sie den konfigurierten Hostnamen, die aufgelöste Adresse, den Port und den Server-Listener. Kontrollieren Sie, ob der Dienst an eine für den Client erreichbare Adresse gebunden ist.
  3. Die Route ist nicht verfügbar. Prüfen Sie die DNS-Auflösung und das Netzwerk-Routing aus der Client-Umgebung, gegebenenfalls einschließlich der Container- oder Cloud-Netzwerkkonfiguration.
  4. Eine Firewall oder eine Richtlinie auf der Gegenstelle filtert den Verkehr. Vergleichen Sie den Zugriff aus demselben Client-Netzwerk mit dem Zugriff aus einem zugelassenen Netzwerk und prüfen Sie relevante Firewall-Regeln oder Dienstprotokolle.
  5. Der Server ist langsam, ausgefallen oder überlastet. Prüfen Sie seinen Zustand und die Protokolle zum Zeitpunkt des Verbindungsversuchs. Ein Verbindungs-Timeout beweist weder, dass der Server betriebsbereit ist, noch, dass das Timeout das einzige Problem darstellt.

Eine abgelehnte Verbindung und eine Zeitüberschreitung können auf unterschiedliche Netzwerkresultate hindeuten. Meldungen unterscheiden sich jedoch je nach Betriebssystem und Netzwerkpfad. Notieren Sie vor Änderungen das Ziel, den Vorgang, die verstrichene Zeit, das konfigurierte Zeitlimit und die ursprünglichen Details der Ausnahme.

Verbindungs-Timeout für ein Client-Socket festlegen

Erstellen Sie ein noch nicht verbundenes Socket und rufen Sie dann Socket.connect mit dem Ziel und einem positiven, endlichen Zeitlimit auf. Die wesentlichen Aufrufe sind beispielsweise Socket client = new Socket(); und client.connect(new InetSocketAddress(host, port), 5000);. Verwenden Sie try-with-resources, damit das Socket nach der Verwendung geschlossen wird, und halten Sie Host und Port in Diagnoseprotokollen fest.

Führen Sie dieses eigenständige Beispiel aus, um zu sehen, wie ein Client eine Verbindung zu einem lokalen Server an einem dynamisch zugewiesenen Port herstellt. Es verwendet ein Verbindungszeitlimit von einer Sekunde; der Server nimmt einen Client an, und beide Sockets werden am Ende des Blocks geschlossen. Die im Beispiel angezeigte Bezeichnung bleibt unverändert auf Englisch.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

import java.net.ServerSocket;
import java.net.Socket;

public class LocalConnect {
    public static void main(String[] args) throws Throwable {
        try (ServerSocket server = new ServerSocket(0)) {
            Thread worker = new Thread(() -> {
                try {
                    server.accept().close();
                } catch (Throwable ignored) {
                }
            });
            worker.start();
            try (Socket client = new Socket()) {
                client.connect(new java.net.InetSocketAddress("127.0.0.1", server.getLocalPort()), 1000);
                System.out.println("Connected to local server");
            }
            worker.join();
        }
    }
}
Connected to local server

Der bereitgestellte Lauf wurde mit Exitcode 0 abgeschlossen und gab die gezeigte Zeile aus. Die angezeigte Bezeichnung bleibt auf Englisch. Dieser lokale Erfolg veranschaulicht das API-Muster, beweist aber nicht, dass ein entfernter Produktionshost erreichbar ist.

Accept-Timeout für ein Server-Socket festlegen

Rufen Sie ServerSocket.setSoTimeout mit einer positiven Millisekundenangabe auf, bevor Sie accept() aufrufen. server.setSoTimeout(10000); begrenzt beispielsweise, wie lange server.accept() auf einen Client wartet. Kommt innerhalb dieses Zeitraums kein Client an, endet der wartende Accept-Vorgang mit SocketTimeoutException. Das Server-Socket bleibt weiterhin verwendbar, sodass eine Serverschleife das Timeout abfangen und fortfahren oder eine Leerlaufzeit melden kann. Schließen Sie das angenommene Client-Socket und das lauschende Server-Socket, sobald Sie sie nicht mehr benötigen. Diese Einstellung begrenzt weder die Lebensdauer der angenommenen Verbindung noch die Wartezeit späterer Lesevorgänge.

Dieses eigenständige Beispiel enthält absichtlich keinen verbindenden Client. Es zeigt, wie die Accept-Frist abläuft, und prüft anschließend, dass das lauschende Socket weiterhin geöffnet ist. Die im Beispiel angezeigten Bezeichnungen und Ausgaben bleiben unverändert auf Englisch.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

import java.net.ServerSocket;
import java.net.SocketTimeoutException;

public class AcceptDeadline {
    public static void main(String[] args) throws Exception {
        try (ServerSocket server = new ServerSocket(0)) {
            server.setSoTimeout(100);
            try {
                server.accept();
                System.out.println("Unexpected client connection");
            } catch (SocketTimeoutException timeout) {
                System.out.println("Accept deadline reached");
            }
            System.out.println("Listening socket open: " + !server.isClosed());
        }
    }
}
Accept deadline reached
Listening socket open: true

Der bereitgestellte Lauf wurde mit Exitcode 0 abgeschlossen und gab die beiden gezeigten Zeilen aus. Die angezeigten Bezeichnungen bleiben auf Englisch.

Timeouts für HttpURLConnection festlegen

Rufen Sie bei einer als HttpURLConnection geöffneten URL-Verbindung HttpURLConnection.setConnectTimeout auf, um den Verbindungsaufbau zu begrenzen, und setReadTimeout, um die Wartezeit auf Antwortdaten zu begrenzen. Wandeln Sie beispielsweise url.openConnection() in HttpURLConnection um und verwenden Sie vor dem Verbindungsaufbau oder Lesen connection.setConnectTimeout(8000); und connection.setReadTimeout(10000);. Die Werte werden in Millisekunden angegeben; null bedeutet kein Zeitlimit. Lesen Sie die Antwort und schließen Sie ihren Stream mit try-with-resources. Rufen Sie anschließend disconnect() auf, wenn die Anfrage abgeschlossen ist. Fangen Sie SocketTimeoutException für eine gezielte Behandlung von Zeitüberschreitungen und andere IOException-Fehler für allgemeinere Netzwerkfehler ab. Der Klassenname lautet genau HttpURLConnection.

SocketTimeoutException behandeln und die Lösung überprüfen

Fangen Sie SocketTimeoutException um den Vorgang herum ab, bei dem das Zeitlimit auftreten kann, und protokollieren Sie hilfreiche Angaben wie Host, Port, Vorgang, konfiguriertes Zeitlimit und verstrichene Zeit. Wiederholen Sie den Vorgang nicht unbegrenzt und unbemerkt: Verwenden Sie eine begrenzte Wiederholungsstrategie nur, wenn eine Wiederholung sicher ist, und halten Sie jeden Versuch innerhalb der Gesamtfrist der Anfrage. Schließen Sie Sockets und Streams in finally-Blöcken oder mit try-with-resources, damit ein fehlgeschlagener Versuch keine Ressourcen offen lässt.

Prüfen Sie nach einer Änderung die Verbindung aus derselben Laufzeitumgebung, in der der Fehler auftrat. Bestätigen Sie Adresse und Port, prüfen Sie, ob der Server lauscht und erreichbar ist, und testen Sie den konkreten Vorgang: Verbindungsaufbau, Lesen oder Annehmen einer Verbindung. Vergleichen Sie die gemessene Dauer mit dem konfigurierten Zeitlimit und prüfen Sie die serverseitigen Protokolle. Verzögert ein längeres Zeitlimit lediglich denselben Fehler, setzen Sie ein angemessenes Zeitlimit und untersuchen Sie stattdessen Routing, Filterung, den Zustand des Listeners oder den Zustand des Dienstes. Ein erfolgreicher Test mit einem lokalen Server belegt nicht, dass ein entfernter Endpunkt oder ein Produktionsnetzwerk erreichbar ist.