Como posso interromper um método ServerSocket accept ()?


143

No meu thread principal, tenho um while(listening)loop que chama accept()meu objeto ServerSocket, inicia um novo thread do cliente e o adiciona a uma coleção quando um novo cliente é aceito.

Também tenho um thread de administração que quero usar para emitir comandos, como 'exit', que fará com que todos os threads do cliente sejam desligados, desligados e desligados do thread principal, tornando a escuta false.

No entanto, a accept()chamada no while(listening)loop é bloqueada e não parece haver nenhuma maneira de interrompê-la, portanto a condição while não pode ser verificada novamente e o programa não pode sair!

Existe uma maneira melhor de fazer isso? Ou alguma maneira de interromper o método de bloqueio?



Para o pepole que deseja esperar x tempo e depois reagir, use setSoTimeout ().
— Abdullah Orabi 22/01

Respostas:


151

Você pode ligar close()de outro segmento, e a accept()chamada emitirá a SocketException.


2
Obrigado, tão óbvio, nem me ocorreu! Eu estava ligando para close () depois de sair do loop.
— lukeo05

4
Estranho, o fato de não haver essas informações nos documentos: o método download.oracle.com/javase/6/docs/api/java/net/… não está marcado como lançando SocketException. Ele é mencionado apenas aqui download.oracle.com/javase/1.4.2/docs/api/java/net/…
— Vladislav Rastrusny

1
É bom ligar close(), quero dizer, isso lançará a exceção ao fazê-lo; portanto, existe outra maneira (que não gera exceção, também não baseada no tempo limite) para parar de atender a solicitações?
— Kushal

3
E o que fazer se a conexão já tiver sido aceita e o encadeamento estiver aguardando alguns dados: while ((s = in.readLine ())! = Null)?
— Alex Fedulov

1
@AlexFedulov Desligue esse soquete para entrada. readLine()retornará nulo e ocorrerão as operações normais de fechamento que o encadeamento já deve ter.
— Marquês de Lorne

31

Ative o tempo limite accept(), a chamada atingirá o tempo limite do bloqueio após o tempo especificado:

http://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT

Defina um tempo limite para Socketoperações de bloqueio :

ServerSocket.accept();
SocketInputStream.read();
DatagramSocket.receive();

A opção deve ser definida antes da entrada em uma operação de bloqueio para entrar em vigor. Se o tempo limite expirar e a operação continuar bloqueando, java.io.InterruptedIOExceptionserá gerado. O Socketnão está fechado neste caso.



4

Você pode simplesmente criar um soquete "void" para quebrar os servidores socket.accept ()

Lado do servidor

private static final byte END_WAITING = 66;
private static final byte CONNECT_REQUEST = 1;

while (true) {
      Socket clientSock = serverSocket.accept();
      int code = clientSock.getInputStream().read();
      if (code == END_WAITING
           /*&& clientSock.getInetAddress().getHostAddress().equals(myIp)*/) {
             // End waiting clients code detected
             break;
       } else if (code == CONNECT_REQUEST) { // other action
           // ...
       }
  }

Método para interromper o ciclo do servidor

void acceptClients() {
     try {
          Socket s = new Socket(myIp, PORT);
          s.getOutputStream().write(END_WAITING);
          s.getOutputStream().flush();
          s.close();
     } catch (IOException e) {
     }
}

4

A razão ServerSocket.close()lança uma exceção é porque você tem um outputstreamou um inputstream anexo a esse soquete. Você pode evitar essa exceção com segurança fechando primeiro os fluxos de entrada e saída. Em seguida, tente fechar o ServerSocket. Aqui está um exemplo:

void closeServer() throws IOException {
  try {
    if (outputstream != null)
      outputstream.close();
    if (inputstream != null)
      inputstream.close();
  } catch (IOException e1) {
    e1.printStackTrace();
  }
  if (!serversock.isClosed())
    serversock.close();
  }
}

Você pode chamar esse método para fechar qualquer soquete de qualquer lugar sem obter uma exceção.


7
Os fluxos de entrada e saída não estão anexados ao, ServerSocketmas ao a, Sockete estamos falando de fechar o ServerSocketnot the Socket, para que ele ServerSocketpossa ser fechado sem fechar Socketos fluxos de a.
— icza 3/09/14


1

OK, consegui isso trabalhando de uma maneira que resolva a questão do OP mais diretamente.

Continue lendo a resposta curta para um exemplo de Thread de como eu uso isso.

Resposta curta:

ServerSocket myServer;
Socket clientSocket;

  try {    
      myServer = new ServerSocket(port)
      myServer.setSoTimeout(2000); 
      //YOU MUST DO THIS ANYTIME TO ASSIGN new ServerSocket() to myServer‼!
      clientSocket = myServer.accept();
      //In this case, after 2 seconds the below interruption will be thrown
  }

  catch (java.io.InterruptedIOException e) {
      /*  This is where you handle the timeout. THIS WILL NOT stop
      the running of your code unless you issue a break; so you
      can do whatever you need to do here to handle whatever you
      want to happen when the timeout occurs.
      */
}

Exemplo do mundo real:

Neste exemplo, eu tenho um ServerSocket aguardando uma conexão dentro de um Thread. Quando fecho o aplicativo, desejo desligar o encadeamento (mais especificamente, o soquete) de maneira limpa antes de deixá-lo fechar, então uso o .setSoTimeout () no ServerSocket e depois a interrupção acionada após o tempo limite para verificar e verificar se o pai está tentando desligar o encadeamento. Nesse caso, defino o soquete, defino um sinalizador indicando que o encadeamento está concluído e interrompo o loop do encadeamento, que retorna um valor nulo.

package MyServer;

import javafx.concurrent.Task;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javafx.concurrent.Task;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

public class Server {

public Server (int port) {this.port = port;}

private boolean      threadDone        = false;
private boolean      threadInterrupted = false;
private boolean      threadRunning     = false;
private ServerSocket myServer          = null;
private Socket       clientSocket      = null;
private Thread       serverThread      = null;;
private int          port;
private static final int SO_TIMEOUT    = 5000; //5 seconds

public void startServer() {
    if (!threadRunning) {
        serverThread = new Thread(thisServerTask);
        serverThread.setDaemon(true);
        serverThread.start();
    }
}

public void stopServer() {
    if (threadRunning) {
        threadInterrupted = true;
        while (!threadDone) {
            //We are just waiting for the timeout to exception happen
        }
        if (threadDone) {threadRunning = false;}
    }
}

public boolean isRunning() {return threadRunning;}


private Task<Void> thisServerTask = new Task <Void>() {
    @Override public Void call() throws InterruptedException {

        threadRunning = true;
        try {
            myServer = new ServerSocket(port);
            myServer.setSoTimeout(SO_TIMEOUT);
            clientSocket = new Socket();
        } catch (IOException e) {
            e.printStackTrace();
        }
        while(true) {
            try {
                clientSocket = myServer.accept();
            }
            catch (java.io.InterruptedIOException e) {
                if (threadInterrupted) {
                    try { clientSocket.close(); } //This is the clean exit I'm after.
                    catch (IOException e1) { e1.printStackTrace(); }
                    threadDone = true;
                    break;
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
};

}

Então, na minha classe Controller ... (mostrarei apenas o código relevante, massageie-o no seu próprio código, conforme necessário)

public class Controller {

    Server server = null;
    private static final int port = 10000;

    private void stopTheServer() {
        server.stopServer();
        while (server.isRunning() {
        //We just wait for the server service to stop.
        }
    }

    @FXML private void initialize() {
        Platform.runLater(()-> {
            server = new Server(port);
            server.startServer();
            Stage stage = (Stage) serverStatusLabel.getScene().getWindow();
            stage.setOnCloseRequest(event->stopTheServer());
        });
    }

}

Espero que isso ajude alguém no caminho.


0

Outra coisa que você pode tentar, que é mais limpa, é verificar um sinalizador no loop de aceitação e, quando o thread do administrador desejar eliminar o bloqueio de threads no accept, defina o sinalizador (torne o thread seguro) e faça um soquete de cliente ligação à tomada de audição. A aceitação interromperá o bloqueio e retornará o novo soquete. Você pode descobrir alguma coisa simples sobre o protocolo que diz ao thread de escuta para sair do thread de maneira limpa. E, em seguida, feche o soquete no lado do cliente. Sem exceções, muito mais limpo.


isso não faz sentido ... quando você tem um código como este socket = serverSocket.accept (); naquele momento, o bloqueio é iniciado e o loop não faz parte do nosso código; portanto, há uma maneira de fazer com que o código de bloqueio procure um sinalizador que eu defini ... pelo menos não consegui encontrar uma maneira de fazer isso. .. se você tem código de trabalho, por favor, compartilhe?
— Michael Sims

Sim, parece que deixei de fora uma informação crucial que faria com que não fizesse muito sentido. Você precisaria tornar o soquete de aceitação sem bloqueio e apenas bloquear por um período de tempo antes da repetição do loop, em que área você verificaria o sinalizador de saída.
— 22418 Stu

Como exatamente você faz o soquete de aceitação, sem bloqueio?
— Michael Sims

long = 1L; if (ioctl (socket, (int) FIONBIO, (char *) & on)))
— stu

@stu Esta pergunta é para os soquetes do Java.
— Kröw 23/07/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.