如何中断ServerSocket accept()方法?

时间:2023-02-13 15:06:57

In my main thread I have a while(listening) loop which calls accept() on my ServerSocket object, then starts a new client thread and adds it to a Collection when a new client is accepted.

在我的主线程中,我有一个while(监听)循环,它在我的ServerSocket对象上调用accept(),然后启动一个新的客户端线程,并在接受新客户端时将其添加到集合中。

I also have an Admin thread which I want to use to issue commands, like 'exit', which will cause all the client threads to be shut down, shut itself down, and shut down the main thread, by turning listening to false.

我还有一个管理线程,我想用它来发出命令,比如“exit”,它会导致所有的客户端线程被关闭,关闭自己,关闭主线程,让听错。

However, the accept() call in the while(listening) loop blocks, and there doesn't seem to be any way to interrupt it, so the while condition cannot be checked again and the program cannot exit!

但是,在while(监听)循环中调用accept()会阻塞,而且似乎没有任何方法可以中断它,因此无法再次检查while条件,程序无法退出!

Is there a better way to do this? Or some way to interrupt the blocking method?

有更好的方法吗?或者用什么方法来中断阻塞方法?

8 个解决方案

#1


130  

You can call close() from another thread, and the accept() call will throw a SocketException.

您可以从另一个线程调用close(), accept()调用将抛出一个SocketException。

#2


28  

Set timeout on accept(), then the call will timeout the blocking after specified time:

Set timeout on accept(),然后调用在指定时间后超时阻塞:

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

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

Set a timeout on blocking Socket operations:

设置阻塞套接字操作的超时:

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

The option must be set prior to entering a blocking operation to take effect. If the timeout expires and the operation would continue to block, java.io.InterruptedIOException is raised. The Socket is not closed in this case.

必须在进入阻塞操作之前设置该选项,才能生效。如果超时过期且操作将继续阻塞,那么java.io。InterruptedIOException。在这种情况下插座不闭合。

#3


8  

Is calling close() on the ServerSocket an option?

在ServerSocket上调用close()是一个选项吗?

http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html#close%28%29

http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html近% 29 28%

Closes this socket. Any thread currently blocked in accept() will throw a SocketException.

关闭套接字。当前在accept()中阻塞的任何线程都将抛出一个SocketException。

#4


3  

You can just create "void" socket for break serversocket.accept()

您可以为break serversocket创建“void”套接字。accept()

Server side

服务器端

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
           // ...
       }
  }

Method for break server cycle

中断服务器周期的方法

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

#5


3  

The reason ServerSocket.close() throws an exception is because you have an outputstream or an inputstream attached to that socket. You can avoid this exception safely by first closing the input and output streams. Then try closing the ServerSocket. Here is an example:

close()抛出异常的原因是您有一个outputstream或inputstream附加到该套接字上。您可以通过首先关闭输入和输出流来安全地避免此异常。然后尝试关闭ServerSocket。这是一个例子:

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();
  }
}

You can call this method to close any socket from anywhere without getting an exception.

您可以调用此方法来从任何地方关闭任何套接字,而不会出现异常。

#6


0  

Another thing you can try which is cleaner, is to check a flag in the accept loop, and then when your admin thread wants to kill the thread blocking on the accept, set the flag (make it thread safe) and then make a client socket connection to the listening socket. The accept will stop blocking and return the new socket. You can work out some simple protocol thing telling the listening thread to exit the thread cleanly. And then close the socket on the client side. No exceptions, much cleaner.

您可以尝试的另一种更干净的方法是检查accept循环中的标志,然后当您的管理线程想要杀死accept上阻塞的线程时,设置标志(使其线程安全),然后将客户端套接字连接到侦听套接字。accept将停止阻塞并返回新的套接字。您可以制定一些简单的协议,告诉监听线程干净地退出线程。然后关闭客户端上的套接字。没有例外,干净了很多。

#7


0  

Use serverSocket.setSoTimeout(timeoutInMillis).

使用serverSocket.setSoTimeout(timeoutInMillis)。

#8


0  

OK, I got this working in a way that addresses the OP's question more directly.

我用一种更直接的方式解决了OP的问题。

Keep reading past the short answer for a Thread example of how I use this.

请继续阅读我如何使用这个线程示例的简短答案。

Short answer:

简短的回答:

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.
      */
}

Real world example:

现实世界的例子:

In this example, I have a ServerSocket waiting for a connection inside a Thread. When I close the app, I want to shut down the thread (more specifically, the socket) in a clean manner before I let the app close, so I use the .setSoTimeout() on the ServerSocket then I use the interrupt that is thrown after the timeout to check and see if the parent is trying to shut down the thread. If so, then I set close the socket, then set a flag indicating that the thread is done, then I break out of the Threads loop which returns a null.

在本例中,我有一个ServerSocket,在线程中等待连接。当我关闭应用程序,我想关闭线程(更具体地说,套接字)清洁的方式让应用程序关闭之前,所以我使用.setSoTimeout()在考察一下然后我使用中断后抛出超时检查,看看父母试图关闭线程。如果是,那么我将关闭套接字,然后设置一个标志,指示线程已经完成,然后我将跳出返回null的线程循环。

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;
    }
};

}

Then, in my Controller class ... (I will only show relevant code, massage it into your own code as needed)

然后,在我的控制器类中……(我只显示相关代码,根据需要将其转换为您自己的代码)

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());
        });
    }

}

I hope this helps someone down the road.

我希望这能帮助别人。

#1


130  

You can call close() from another thread, and the accept() call will throw a SocketException.

您可以从另一个线程调用close(), accept()调用将抛出一个SocketException。

#2


28  

Set timeout on accept(), then the call will timeout the blocking after specified time:

Set timeout on accept(),然后调用在指定时间后超时阻塞:

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

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

Set a timeout on blocking Socket operations:

设置阻塞套接字操作的超时:

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

The option must be set prior to entering a blocking operation to take effect. If the timeout expires and the operation would continue to block, java.io.InterruptedIOException is raised. The Socket is not closed in this case.

必须在进入阻塞操作之前设置该选项,才能生效。如果超时过期且操作将继续阻塞,那么java.io。InterruptedIOException。在这种情况下插座不闭合。

#3


8  

Is calling close() on the ServerSocket an option?

在ServerSocket上调用close()是一个选项吗?

http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html#close%28%29

http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html近% 29 28%

Closes this socket. Any thread currently blocked in accept() will throw a SocketException.

关闭套接字。当前在accept()中阻塞的任何线程都将抛出一个SocketException。

#4


3  

You can just create "void" socket for break serversocket.accept()

您可以为break serversocket创建“void”套接字。accept()

Server side

服务器端

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
           // ...
       }
  }

Method for break server cycle

中断服务器周期的方法

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

#5


3  

The reason ServerSocket.close() throws an exception is because you have an outputstream or an inputstream attached to that socket. You can avoid this exception safely by first closing the input and output streams. Then try closing the ServerSocket. Here is an example:

close()抛出异常的原因是您有一个outputstream或inputstream附加到该套接字上。您可以通过首先关闭输入和输出流来安全地避免此异常。然后尝试关闭ServerSocket。这是一个例子:

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();
  }
}

You can call this method to close any socket from anywhere without getting an exception.

您可以调用此方法来从任何地方关闭任何套接字,而不会出现异常。

#6


0  

Another thing you can try which is cleaner, is to check a flag in the accept loop, and then when your admin thread wants to kill the thread blocking on the accept, set the flag (make it thread safe) and then make a client socket connection to the listening socket. The accept will stop blocking and return the new socket. You can work out some simple protocol thing telling the listening thread to exit the thread cleanly. And then close the socket on the client side. No exceptions, much cleaner.

您可以尝试的另一种更干净的方法是检查accept循环中的标志,然后当您的管理线程想要杀死accept上阻塞的线程时,设置标志(使其线程安全),然后将客户端套接字连接到侦听套接字。accept将停止阻塞并返回新的套接字。您可以制定一些简单的协议,告诉监听线程干净地退出线程。然后关闭客户端上的套接字。没有例外,干净了很多。

#7


0  

Use serverSocket.setSoTimeout(timeoutInMillis).

使用serverSocket.setSoTimeout(timeoutInMillis)。

#8


0  

OK, I got this working in a way that addresses the OP's question more directly.

我用一种更直接的方式解决了OP的问题。

Keep reading past the short answer for a Thread example of how I use this.

请继续阅读我如何使用这个线程示例的简短答案。

Short answer:

简短的回答:

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.
      */
}

Real world example:

现实世界的例子:

In this example, I have a ServerSocket waiting for a connection inside a Thread. When I close the app, I want to shut down the thread (more specifically, the socket) in a clean manner before I let the app close, so I use the .setSoTimeout() on the ServerSocket then I use the interrupt that is thrown after the timeout to check and see if the parent is trying to shut down the thread. If so, then I set close the socket, then set a flag indicating that the thread is done, then I break out of the Threads loop which returns a null.

在本例中,我有一个ServerSocket,在线程中等待连接。当我关闭应用程序,我想关闭线程(更具体地说,套接字)清洁的方式让应用程序关闭之前,所以我使用.setSoTimeout()在考察一下然后我使用中断后抛出超时检查,看看父母试图关闭线程。如果是,那么我将关闭套接字,然后设置一个标志,指示线程已经完成,然后我将跳出返回null的线程循环。

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;
    }
};

}

Then, in my Controller class ... (I will only show relevant code, massage it into your own code as needed)

然后,在我的控制器类中……(我只显示相关代码,根据需要将其转换为您自己的代码)

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());
        });
    }

}

I hope this helps someone down the road.

我希望这能帮助别人。