有效地停止ServerSockets

时间:2016-01-05 18:51:23

标签: java networking

我是网络新手,我一直在玩Java中的套接字。我想知道停止ServerSocket的最佳方法是什么。使用我当前的代码,我收到以下错误:

java.net.SocketException: socket closed
[13:30:50] [pool-4-thread-1/WARN]:   at java.net.DualStackPlainSocketImpl.accept0(Native Method)
[13:30:50] [pool-4-thread-1/WARN]:  at java.net.DualStackPlainSocketImpl.socketAccept(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.net.AbstractPlainSocketImpl.accept(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.net.PlainSocketImpl.accept(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.net.ServerSocket.implAccept(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.net.ServerSocket.accept(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at io.bluecube.worldlink.DataListener.run(DataListener.java:35) //<------------------------
[13:30:50] [pool-4-thread-1/WARN]:  at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.util.concurrent.FutureTask.run(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
[13:30:50] [pool-4-thread-1/WARN]:  at java.lang.Thread.run(Unknown Source)

错误指向我的DataListener类中的这一行:

public class DataListener implements Runnable{

private ServerSocket socket;

public DataListener(ServerSocket socket){
    this.socket = socket;
}

@Override
public void run() {
    while (!Thread.currentThread().isInterrupted()){
        try {
            Socket receive = socket.accept(); //<---- Error
     //stuff 

此runnable在线程池中运行。目前我用以下内容关闭了所有内容:

private ExecutorService pool = Executors.newCachedThreadPool();
private ServerSocket serverSocket;

//stuff

public void shutdown() throws InterruptedException{
    pool.shutdownNow();
    if (!serverSocket.isClosed()){
        try {
            this.serverSocket.close();
        } catch(IOException e1) {
            e1.printStackTrace();
        }
    }
    if (pool.awaitTermination(10, TimeUnit.SECONDS)){
        System.out.println("Terminated");
    }
}

有没有更好的方法来关闭它?谢谢!

2 个答案:

答案 0 :(得分:0)

您的问题是,除非另有说明,否则accept()方法将无限期阻止。所以,是的,在accept()阻塞时关闭套接字会导致你看到的问题。

首先致电setSoTimeout()上的ServerSocket。这将使accept()仅阻止N毫秒......之后它会抛出一个你必须处理的SocketTimeoutException,但这可以让你有效地将它放入循环中 - 只需在每次运行结束时重新提交Runnable即可。如果游泳池已关闭,您将无法重新提交,并且您已经知道您已完成。

接下来,在池上调用shutdown()之后,您需要等待池实际关闭。在池中使用awaitTermination(),然后您可以关闭ServerSocket

当然,有很多变化 - 您可以保持while()循环并设置某种标志等 - 无论您最熟悉的是什么。我上面提到的大纲只是我想到的第一件事,它可能不适合你,但希望能为你指路。

答案 1 :(得分:0)

试着回答:

  

有没有更好的方法来关闭它?

请勿使用!Thread.currentThread().isInterrupted()作为您的条件,因为它不可靠。

<强> 为什么?

首先,请查看(阅读)shutdown()shutdownNow()及其shutdownNow()

  

此方法不等待主动执行任务终止。使用awaitTermination来做到这一点。

另外还有pool.shutdownNow();

  

除了尽力尝试停止处理主动执行任务之外,没有任何保证。例如,典型的实现将通过Thread.interrupt()取消,因此任何无法响应中断的任务都可能永远不会终止。

因此,调用this.serverSocket.close();可能会或可能不会产生(立即)效果。它也是一个非阻塞调用,这意味着!Thread.currentThread().isInterrupted()可能会在任何其他线程继续执行之前执行。

也可能发生 线程A评估shutdown()然后被暂停以让线程B继续。线程B运行serverSocket方法关闭(!)serverSocket。线程A恢复时,DataListener已关闭。 - 您不应该假设任何特定的执行顺序。

您根本不知道/控制//stuff线程何时被中断。实际上,根据您的实施(serverSocket),它们可能永远不会被中断。

那么现在呢?

  • 而不是在多个线程之间共享Socket,而是将您从serverSocket.accept()DataListener个主题的新serverSocket实例传递给您。
  • shutdown()serverSocket.accept()方法保持在同一个类(主题)中,并将pool.execute(new DataListener(serverSocket.accept()));上的“while-true-loop”阻止与<{1}}保持一致:

    serverSocket

  • (考虑为DataListener定义超时。)

通过这种方式,您只为已连接的Socket实例启动// stuff个线程。使用该连接执行Socket,然后关闭该线程中的public class DataListener implements Runnable { private final Socket receive; public DataListener(final Socket receive){ this.receive = receive; } @Override public void run() { // do stuff with your connected socket // do proper resource/exception handling // close the socket here } } 实例。

try {
  ...
}
catch (IOException e) {
  connection.getErrorStream().close();
}