如何结束线程处理套接字连接?

时间:2016-12-31 12:22:44

标签: java multithreading sockets blocking

我有一个处理套接字连接的线程:

BufferedReader socketInput = new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
while (true)
{
    String line = socketInput.readLine();
    // do stuff
}

正如我在本网站上的几个答案中所读到的,推荐的解决方案是使用一个标志,一个线程设置并且我的(套接字处理)线程在该标志改变状态时检查并终止自身。类似的东西:

while (!done)
{
    String line = socketInput.readLine();
    // do stuff
}

但是当readLine()仍在等待输入时,这可能会卡住。我想我可以设置超时:

mySocket.setSoTimeout(100);
while (!done)
{
    String line = socketInput.readLine();
    // do stuff
}

哪个可能会有效但我仍然会在我的线程之前得到100毫秒的延迟"意识到"国旗的状态发生了变化。

线程有没有办法实现"它应该结束吗?如果没有,我的解决方案(超时和标志done)是否正确?

修改:我已澄清socketInput类型为BufferedReader(或者我考虑Scanner)。

4 个答案:

答案 0 :(得分:3)

处理此问题的最常用方法是从另一个Thread关闭套接字。这将导致读取端解除阻塞并退出套接字关闭的(预期)错误。根据您可用的套接字API,也可能仅关闭读取端。简而言之,JDK shutdownInput()可能会起作用。

如果您希望稍后继续从套接字读取这些obvisouly将无法正常工作。你的解决方案应该在那里工作,但是因为你基本上在100毫秒内轮询套接字,所以性能和反应性显然更糟。

答案 1 :(得分:2)

解决方案是正确的,当完成设置为true时它将退出。 是的,readLine将始终等待100ms,如果你不想等待,你可以通过调用thread.interrupt()来中断线程,但它不是很干净的方式。

答案 2 :(得分:2)

  1. 创建选择器
  2. 使用SelectionKey.OP_READ
  3. socket.getChannel() non-blockingregister it配置到选择器
  4. 调用您的选择器select()方法,该方法会在有一些数据需要阅读时返回,以便您拨打readLine()(即select()返回> 0
  5. 每当您想要结束套接字处理时,请设置done标志并调用您的选择器wakeup()方法。这将使select()立即返回(可能为0,如果有活动则为1)。然后,您可以检查done标志并正常结束线程。

    这是一个快速实施。请注意,我将BufferedReader作为参数传递,就像您在线程中打开它一样,您也应该将其关闭,这也会关闭套接字,因此必须在外部完成。有两种方法可以通知线程正常停止处理输入,另一种方法是发送数据:

    public class SocketHandler extends Thread {
    
        private Socket sok;
        private BufferedReader socketInput;
    
        private Selector sel;
        private SocketChannel chan;
        private boolean done;
    
        public SocketHandler(Socket sok, BufferedReader socketInput) throws IOException {
            this.sok = sok;
            chan = sok.getChannel();
            chan.configureBlocking(false);
            sel = Selector.open();
            chan.register(sel, SelectionKey.OP_READ);
            this.socketInput = socketInput;
            done = false;
        }
    
        @Override
        public void run() {
            while (!done) {
                try {
                    if (sel.select() == 0)
                        continue;
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                // Only one channel is registered on only one operation so we know exactly what happened.
                sel.selectedKeys().clear();
                doRead();
                // Otherwise: loop through sel.selectedKeys(), check for readability and clear the set
            }
            try {
                sel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void doRead() {
            try {
                String line = socketInput.readLine();
                // TODO: process 'line'
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public void signalStop() {
            done = true;
            if (sel != null)
                sel.wakeup(); // Get out of sel.select()
        }
    
        public void doWrite(byte[] buffer) throws IOException { // Or "String message"
            sok.getOutputStream().write(buffer); // Or anything else
        }
    
    }
    

答案 3 :(得分:0)

了解何时完成套接字连接的最佳方法是尝试读取内容。如果read方法返回-1,则可以结束线程套接字连接

byte[] data = new byte[2048];
while (!done) {
    int count = input.read(data);
    if (count <= 0) {
        if (count < 0)
            done = true;
        continue;
    }
    String request = new String(data, 0, count);
    //do stuff
}

如果count == -1,我们尝试读取输入中的内容,套接字客户端现在断开连接,现在我们可以通过更改done的值来结束循环。