Java ServerSocket.accept() elegant unterbrechen

mental.dIseASe

Lieutenant
Registriert
Dez. 2008
Beiträge
674
Guten Abend,

ich bin gerade dabei, so etwas wie einen Application-Server zu entwickeln. Dieser ist zweigeteilt: auf der einen Seite habe ich so etwas wie einen Admin-Query-Thread, der auf einem Port auf Admin-Anfragen lauscht(zb. den Call, den Server als ganzes zu beenden). Auf der anderen Seite habe ich einen Thread, der auf einem anderen Port lauscht, um die eigentlichen Anfragen an die Application zu beantworten. Ich lausche in einer Schleife und delegiere die TCP-Connections dann in Worker-Threads eines Pools.

Code:
private class ServiceQueryThread extends Thread {

private final ServerSocket _serverSocket;
private final ExecutorService _pool;

public ServiceQueryThread(int listenPort) {
    _serverSocket = new ServerSocket(listenPort);
    _pool = Executors.newFixedThreadPool(20);
}

@Override
public void run() {
    Socket connection = null;
    while(!isInterrupted()){
        connection = _serverSocket.accept();
        _pool.execute(new Handler(connection));
    }
    //finishStuff
}
}
Der start-Code meines Servers sieht dann in etwa so aus:
Code:
public void start() {
    adminThread admin = new adminThread(aPort);
    serviceQueryThread service = new ServiceQueryThread(anotherPort);
    admin.start();
    service.start();
    admin.join(); // Thread ist dann zuende, wenn der Server beendet werden soll
    service.interrupt(); //semantisch: Hoer auf zu lauschen und beende saemtliche Kommunikation
}
Wenn ich also mit dem Admin-Thread fertig bin, dann möchte ich dem ServiceQueryThread sagen, dass er aufhören soll zu lauschen, weil er dann auch demnächst abgewickelt wird. Aber hier liegt genau mein Problem: wenn der Interrupt kommt, bin ich in 99% aller Fälle grad im accept() und blockiere folglich. Leider zählt accept() nicht als blockierende Operation in dem Sinne, dass es beim interrupt() wie sleep() eine InterruptedException schmeisst.

Ich habe bisher 3 Lösungen sondiert, die mir aber irgendwie alle zu dirty sind:

1. statt service.interrupt() aufzurufen, gebe ich dem ServiceQueryThread noch eine Methode kill() oder überschreibe interrupt(), sodass dann der _serverSocket gewaltsam close()d wird. Dadurch wird accept() unterbrochen und ich bekomme eine IOException und kann den Schleifenkopf auswerten. Das gefällt mir aber nicht, weil ich damit die normalen Mechanismen zum Thread beenden unsauber ausheble.

2. ich gebe dem serverSocket mit setSoTimeout einen Timeout und bekomme dann potenziell bei jedem Schleifendurchlauf, in dem keine Verbindungsanfrage eingeht, eine SocketTimeoutException und der Schleifenkopf wird regelmäßig, je nach Timeout, geprüft. Bewusst mit Exceptions zu rechnen und sie böswillig zu erzeugen, ist aber auch blöd.

3. Ich erstelle innerhalb des Servers kurz eine Dummy-Client-Verbindung zum Server, die so das accept() nochmal "klicken" laesst, damit ich zum Schleifenkopf komme. Auch irgendwie billig.

Gibt es bessere Varianten, die mir nicht einfallen? Ich hoffe, ich konnte mich trotz "Wall of Text" halbwegs verständlich ausdrücken und hoffe auf eure Expertise.

Gruß,
mental
 
Variante 3 ist aus meiner Sicht die sinnvollste, vor allem weil du dich auf kein Verhalten bezüglich Interrupts oder Timeouts verlassen musst.
 
Du könntest auch dem Thread von außen sagen, dass er den Socket mit "close()" schließen soll, dann müsstest du um das accept() herum nur eine SocketException abfangen, damit wird auch nicht unbedingt was ausgehebelt, denn du definierst in diesem Fall ja selbst, was bei der Exception passieren soll und benötigst auch keine Schleife mit Timeout.
 
Ok, vielen Dank für den Input bisher. Das mit der SocketException im Speziellen hatte ich übersehen, weil ich direkt IOException als Oberklasse gefangen hatte.

Edit: Den ServerSocket einfach zu closen, scheint wohl Best-Practice zu sein. Zumindest will ich das mal glauben, wenn es auch so bei IBM steht:

Implementing cancelable tasks - Noninterruptible blocking

Ich denke, ich schlaf nochmal ne Nacht drüber und werd das dann wohl so machen. Wie auch immer ich mich dann entscheide: Vielen Dank für euer beide Hilfe, ihr habt mir nochmal wertvolle Denkansätze und Lösungsvorschläge gegeben :)
 
Zuletzt bearbeitet:
Zurück
Oben