5

我有以下 TCP 客户端-服务器通信结构:

  • 在服务器启动时,服务器启动接受器线程,接受客户端连接并将 ServerSocket 传递给它。
  • 当客户端连接到达时,acceptor 线程调用 ServerSocket 上的 accept() 并将客户端处理作业提交给工作线程(通过执行器/线程池)并为其提供客户端套接字。
  • Worker in loop 从客户端套接字流中读取数据,对其进行处理并发送回复。

问题是如何优雅地停止整个系统?我可以通过关闭 ServerSocket 来停止接受线程。它将导致accept() 阻塞调用抛出SocketException。但是如何阻止工人呢?他们从流中读取,这个调用被阻塞了。根据这个流不会抛出 InterruptedException,因此工作者不能被中断()。

看起来我需要从另一个线程关闭工作套接字,对吗?为此,应将套接字设为公共字段,或者应在 worker 中提供关闭它的方法。这会好吗?或者可能是我的整个设计有缺陷?

4

3 回答 3

3

您的模型工作正常。中断像 IO 这样的不可中断结构的最好方法是关闭套接字。你当然可以在进入阻塞状态之前处理它,但是如果 IO 函数对中断没有反应,你真的没有很多好的选择

于 2011-04-11T15:38:54.633 回答
2

您不能简单地停止服务器。关闭过程可能需要一段时间才能进行清理,因为您需要确保一致性。

想象一个数据库服务器,如果您在它执行事务时简单地关闭它,您可能会使其数据不一致。这就是为什么通常需要一段时间才能关闭服务器。

  • 您必须首先停止接受服务器中的新连接。
  • 然后你可以等待当前工作线程完成他们的工作,然后关闭服务器并正式关闭。
  • 或者您强制工作线程关闭它们与
    客户端的连接(可能按照建议使用某种
    标志)。这可能意味着需要进行一些清理以使数据保持一致,例如恢复事务或您在文件或内存中所做的任何类型的更改。

据我了解,在服务器端关闭与客户端的连接应该会导致客户端在他们身边获得 EOF。

[编辑-1]

我对这个问题进行了一些研究,只是因为我有一段时间没有使用套接字,而且我发现这个问题很有趣。我认为正如其他人已经很好指出的那样,唯一的选择是关闭套接字,根据得到的 Javadocs 将自动关闭输入和输出流/

如果线程有可能不是 IO-blocked,而是处于等待状态或休眠状态,我认为仍然建议为给定套接字的相应工作线程发出 Thread.interrupt();因为不能确定每个线程的状态阻塞。

public static class IOServerWorker implements Runnable{

        private Socket socket;

        public IOServerWorker(Socket socket){
            this.socket = socket;
        }

        @Override
        public void run() {
            String line = null;
            try{
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while( (line = reader.readLine())!=null){
                    System.out.println(line);
                }
                reader.close();
            }catch(IOException e){
                //TODO: do cleanup here
                //TODO: log | wrap | rethrow exception
            }
        }
    }
于 2011-04-11T15:38:16.957 回答
2

我建议使用工人定期检查的布尔标志。调用该标志shouldStop,如果它设置为 true,则工人清理然后死亡。遵循此方法将允许您实现一些清理代码,这样您就不会让资源挂起等。

于 2011-04-11T15:35:18.107 回答