我如何中断 ServerSocket 接受()方法?

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

我还有一个管理线程,我想用来发出命令,如 'exit',这将导致所有的客户端线程关闭,关闭本身,并关闭主线程,通过转向监听 false。

但是,while(listening)循环中的 accept()调用阻塞了,而且似乎没有任何方法来中断它,所以 while 条件无法再次检查,程序也无法退出!

有没有更好的方法来做到这一点? 或者某种方法来中断阻塞方法?

111423 次浏览

ServerSocket上调用 close()是一种选择吗?

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

关闭这个套接字。当前阻塞的任何线程都会抛出一个 SocketException。

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

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没有关闭。

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

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

您可以调用此方法从任何地方关闭任何套接字,而无需获得异常。

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

服务器端

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

好的,我让这个工作在某种程度上更直接地回答了 OP 的问题。

请继续阅读以下关于我如何使用该方法的 Thread 示例的简短答案。

简短的回答:

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 在线程中等待连接。当我关闭应用程序时,我希望在关闭应用程序之前以一种干净的方式关闭线程(更具体地说,关闭套接字) ,所以我使用。然后我使用超时后抛出的中断来检查父线程是否试图关闭线程。如果是这样,那么我设置关闭套接字,然后设置一个标志,表明线程完成,然后我打破了线程循环返回一个空。

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


}

我希望这能帮到以后的人。

您可以简单地将超时限制(毫秒)作为参数传递,同时调用接受函数。

接受(1000) ; 1秒后自动关闭请求