Java 有效地停止服务器套接字

Java 有效地停止服务器套接字,java,networking,Java,Networking,我是网络新手,我一直在玩Java中的套接字。我想知道停止服务器套接字的最佳方法是什么。使用当前代码,我得到以下错误: 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.DualSt

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

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)

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

您的问题是,除非另有规定,
accept()
方法将无限期地阻塞。因此,是的,在
accept()
阻塞时关闭套接字会导致出现问题

因此,首先在
服务器套接字上调用
setSortimeout()
。这将使
accept()
仅阻塞N毫秒。。。在此之后,它将抛出一个您必须处理的
SocketTimeoutException
,但这允许您有效地将其放入循环中-只需在每次运行结束时重新提交您的
Runnable
。如果池已关闭,您将无法重新提交,您将知道您已经完成了

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

当然,这方面有很多变化——您可以保持
while()
循环,并设置某种标志,等等——任何您最满意的方式。我上面提供的大纲只是我脑海中的第一件事,对你来说可能并不完美,但希望能为你指明方向。

试图回答:

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

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

为什么?

首先,请参见(阅读)中的,关于
shutdown()
shutdownNow()
它指出:

此方法不会等待主动执行的任务终止。使用此选项可以完成此操作

另外,对于
shutdownNow()

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

因此,调用
pool.shutdownNow()可能有也可能没有(立即)效果。它也是一个非阻塞调用,这意味着
this.serverSocket.close()可能在任何其他线程继续执行之前执行

也可能发生线程A计算
!Thread.currentThread().isInterrupted()
,然后挂起以让线程B继续。线程B运行关闭(!)服务器套接字的
shutdown()
方法。当线程A恢复时,
服务器套接字
关闭。-您不应假定任何特定的执行顺序。

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

那现在怎么办?

  • 将从
    serverSocket.accept()获取的新
    Socket
    实例传递给
    DataListener
    线程,而不是在多个线程之间共享
    serverSocket
  • serverSocket
    shutdown()
    方法以及
    serverSocket.accept()上的“while true循环”阻塞保持在同一类(线程)中:

    pool.execute(新的DataListener(serverSocket.accept())

  • (考虑为您的
    服务器套接字定义超时时间
通过这种方式,您只能为已连接的
套接字
实例启动一个
DataListener
线程。使用该连接执行
//填充
,然后关闭该线程中的
套接字
实例

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

这种方法和任何方法一样好,而且比设置短超时和每次触发时检查布尔值的最佳替代方法更快、更少地浪费CPU。
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");
    }
}
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
    }
}