java - ¿Cómo puedo interrumpir un método ServerSocket accept()?
networking blocking (8)
En mi hilo principal tengo un ciclo while(listening)
que llama a accept()
en mi objeto ServerSocket, luego inicia un nuevo hilo cliente y lo agrega a una Colección cuando se acepta un nuevo cliente.
También tengo un subproceso de administración que quiero usar para emitir comandos, como ''exit'', que hará que todos los subprocesos del cliente se apaguen, se apaguen y cierren el hilo principal, al hacer que la escucha sea falsa.
Sin embargo, la llamada accept()
en los bloques while while(listening)
loop, y no parece haber ninguna forma de interrumpirla, ¡por lo que la condición while no se puede volver a verificar y el programa no puede salir!
¿Hay una mejor manera de hacer esto? ¿O alguna forma de interrumpir el método de bloqueo?
¿Llamar a close()
en ServerSocket
una opción?
http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html#close%28%29
Cierra este conector. Cualquier hilo actualmente bloqueado en accept () lanzará una SocketException.
El motivo por el cual ServerSocket.close()
arroja una excepción se debe a que tiene un outputstream
o un inputstream
asociado a ese socket. Puede evitar esta excepción de forma segura cerrando primero las secuencias de entrada y salida. Luego intente cerrar ServerSocket
. Aquí hay un ejemplo:
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();
}
}
Puede llamar a este método para cerrar cualquier socket desde cualquier lugar sin obtener una excepción.
Establezca el tiempo de espera en accept()
, luego la llamada aguardará el bloqueo después de la hora especificada:
http://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT
Establezca un tiempo de espera para bloquear las operaciones de
Socket
:
ServerSocket.accept(); SocketInputStream.read(); DatagramSocket.receive();
La opción debe configurarse antes de ingresar a una operación de bloqueo para que surta efecto. Si el tiempo de espera expira y la operación continuará
java.io.InterruptedIOException
, sejava.io.InterruptedIOException
. ElSocket
no está cerrado en este caso.
OK, lo hice funcionar de una manera que aborda la pregunta del OP de manera más directa.
Sigue leyendo la respuesta corta para ver un ejemplo de cómo uso esto.
Respuesta corta:
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.
*/
}
Ejemplo del mundo real:
En este ejemplo, tengo un ServerSocket esperando una conexión dentro de un Thread. Cuando cierro la aplicación, quiero cerrar el hilo (más específicamente, el socket) de una manera limpia antes de dejar que la aplicación se cierre, entonces uso el .setSoTimeout () en ServerSocket y luego uso la interrupción que se lanza. después del tiempo de espera para verificar y ver si el padre está tratando de cerrar el hilo. Si es así, configuro el zócalo, establezco un indicador que indica que el hilo está hecho, y luego salgo del bucle de Subprocesos que devuelve un 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;
}
};
}
Luego, en mi clase de controlador ... (solo mostraré el código relevante, lo masajearé en su propio código según sea necesario)
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 esto ayude a alguien en el camino.
Otra cosa que puedes probar que es más limpia es verificar una bandera en el bucle de aceptación y luego cuando tu subproceso de administración quiere eliminar el bloqueo de subprocesos en aceptar, establecer la bandera (hacer que sea segura) y luego hacer una conexión de cliente conexión a la toma de audio. La aceptación detendrá el bloqueo y devolverá el nuevo socket. Puede resolver algo de protocolo simple diciéndole al hilo de escucha que salga del hilo limpiamente. Y luego cierre el socket en el lado del cliente. Sin excepciones, mucho más limpio.
Puede llamar a close()
desde otro subproceso, y la llamada accept()
lanzará una SocketException
.
Puedes simplemente crear un socket "vacío" para romper serversocket.accept ()
Lado del 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 romper el ciclo del servidor
void acceptClients() {
try {
Socket s = new Socket(myIp, PORT);
s.getOutputStream().write(END_WAITING);
s.getOutputStream().flush();
s.close();
} catch (IOException e) {
}
}