2016-12-31 68 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 
    } 

}