2016-12-31 66 views
2

我有一个线程处理的套接字连接:如何结束线程处理套接字连接?

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)是否正确?

编辑:我澄清,socketInputBufferedReader类型(或者我考虑Scanner)的。

+0

参见http://stackoverflow.com/questions/12315149/interrupt-stop-thread-with-socket-io -blocking-operation – Matthieu

+0

我编辑了我的答案,试图证明带有频道的异步I/O并不是什么大不了的事情。我希望你会发现它很有趣,新年快乐! :) – Matthieu

回答

0

知道何时完成套接字连接的最佳方式是尝试读取某些内容。如果read方法返回-1,就可以结束threadling套接字连接

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 
} 

我们尝试读取输入的东西,如果计数== -1,套接字客户端断开连接,现在我们可以结束循环,通过改变价值完成。

+0

我不确定我遵循 - 您的解决方案是否需要连接的另一端才能结束以便我的线程也结束?如果我想从我身边结束连接怎么办? – NPS

2

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

+0

我正在学习套接字和多线程编程,因此干净的解决方案(和高效的解决方案)正是我所追求的目标。所以你说的是,用java.io'就不可能立即结束这个线程,我必须使用java.nio'来代替?你提供的链接:'sel.select(100)' - 这不是和我的代码一样吗?在做任何事之前等待100毫秒? – NPS

+0

你是对的sel(100)也会等待100ms。 –

+0

Javadoc https://docs.oracle.com/javase/7/docs/api/java/nio/channels/Selector.html#select(long)指出等待使用Object.wait(long)完成,所以我认为可以使用selector.notify()通知线程。下面是等待的文件/通知:http://stackoverflow.com/documentation/java/145/object-class-methods-and-constructor/619/wait-and-notify-methods#t=201612311301369525337 –

3

处理此问题的最常见方法是从另一个线程关闭套接字。这将导致读取端解除阻塞并退出,同时关闭套接字的(预期的)错误。根据您可用的套接字API,也可能仅关闭阅读侧。从简短的看JDK shutdownInput()可能工作。

如果你想在这些obvisouly后面继续从套接字读取将无法正常工作。你的解决方案应该在那里工作,但是由于你基本上轮询所有100ms的套接字,所以在性能和反应性方面显然更差。

+0

当然,我可以尝试将100毫秒调整为更短。但有没有更好的方法来做到这一点?假设我想在以后继续使用套接字。并且假设我无法关闭对方的连接(因为它超出了我的控制范围或出于其他原因)。 – NPS

+0

如果你低于100ms,你会得到更多不必要的线程中断(你将得到一个每次抛出的异常),所以你交易快速停止与开销。通常没有用于停止读取和稍后恢复读取的用例,所以接近就足够了。如果有人想暂时停止阅读,那么他不会在套接字上再读取[line],直到他再次对数据感兴趣。你有什么特别的想法? – Matthias247

+0

我想到了一个线程既读取又写入套接字的情况。通常情况下,它等待'read'读取某些数据,但是当它从外部获取数据写入套接字时,它应该停止监听,将数据写入套接字,然后继续等待'read'。 – NPS

2
  1. 创建一个选择
  2. 配置您的socket.getChannel()non-blockingregister itSelectionKey.OP_READ
  3. 的选择打电话给你的选择select()方法时,有一些数据,将返回到阅读,所以你可以调用readLine()(即select()返回> 0

无论何时您想结束插槽处理,请将您的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 
    } 

}