如何中断ServerSocket accept()方法?

时间:2010-06-06 10:21:35

标签: java networking blocking interrupt

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

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

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

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

9 个答案:

答案 0 :(得分:142)

您可以从其他主题中致电close()accept()来电将投出SocketException

答案 1 :(得分:27)

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。在这种情况下,Socket未关闭。

答案 2 :(得分:9)

close()选项上调用ServerSocket吗?

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

  

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

答案 3 :(得分:3)

ServerSocket.close()抛出异常的原因 是因为您有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();
  }
}

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

答案 4 :(得分:3)

你可以创建" void" socket for break serversocket.accept()

服务器端

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

中断服务器周期的方法

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

答案 5 :(得分:1)

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

请继续阅读简短答案,了解我如何使用此内容的主题示例。

简短回答:

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()然后我使用抛出的中断在超时后检查并查看父进程是否正在尝试关闭该线程。如果是这样,那么我设置关闭套接字,然后设置一个标志,指示线程已完成,然后我突破Threads循环,返回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;
    }
};

}

然后,在我的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());
        });
    }

}

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

答案 8 :(得分:-1)

您只需在调用accept函数时将超时限制(毫秒)作为参数传递即可。

例如serverSocket.accept(1000); 1秒后自动关闭请求