首页 文章

如何中断ServerSocket accept()方法?

提问于
浏览
129

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

我还有一个Admin线程,我想用它来发出命令,比如'exit',这将导致所有客户端线程被关闭,自行关闭,并通过转向监听来关闭主线程 .

但是, while(listening) 循环中的 accept() 调用块,并且似乎没有任何方法可以中断它,因此无法再次检查while条件并且程序无法退出!

有一个更好的方法吗?或者某种方式来中断阻塞方法?

8 回答

  • 0

    你可以从另一个线程调用close()accept()调用将抛出SocketException .

  • 3

    accept() 上设置超时,然后调用将在指定时间后超时阻塞:

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

    在阻止Socket操作时设置超时:ServerSocket.accept();
    SocketInputStream.read();
    DatagramSocket.receive();
    必须在进入阻止操作之前设置该选项才能生效 . 如果超时到期并且操作将继续阻塞,则引发java.io.InterruptedIOException . 在这种情况下,套接字未关闭 .

  • 0

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

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

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

  • 3

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

    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) {
         }
    }
    
  • 137

    ServerSocket.close() 抛出 exception 的原因是因为您有一个 outputstreaminputstream 附加到该套接字 . 首先关闭输入和输出流,可以安全地避免此异常 . 然后尝试关闭 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();
      }
    }
    

    您可以调用此方法从任何位置关闭任何套接字而不会出现异常 .

  • 28

    你可以尝试更清洁的另一件事是检查接受循环中的标志,然后当你的管理线程想要杀死接受上的线程阻塞时,设置标志(使其线程安全)然后创建一个客户端套接字连接到侦听套接字 . accept将停止阻塞并返回新套接字 . 你可以找出一些简单的协议,告诉监听线程干净地退出线程 . 然后在客户端关闭套接字 . 没有例外,更清洁 .

  • 9
  • 0

    好的,我的工作方式更直接地解决了OP的问题 .

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

    简短回答:

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

    现实世界的例子:

    在这个例子中,我有一个ServerSocket等待Thread内部的连接 . 当我关闭应用程序时,我想在我关闭应用程序之前以干净的方式关闭线程(更具体地说,套接字),所以我在ServerSocket上使用.setSoTimeout()然后我使用抛出的中断在超时后检查并查看父进程是否正在尝试关闭该线程 . 如果是这样,那么我设置关闭套接字,然后设置一个标志,指示线程已完成,然后我打破了返回null的Threads循环 .

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

    然后,在我的Controller类中...(我只会显示相关代码,根据需要将其按入您自己的代码中)

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

    我希望这可以帮助有人在路上 .

相关问题