2011-03-10 75 views
4

我有一個Java TCP服務器,當客戶端連接到它時,它會每隔30秒向客戶端輸出一條消息。嚴格要求客戶端不向服務器發送任何消息,並且服務器不向客戶端發送除了30秒間隔消息之外的任何數據。檢查Java TCP服務器上的客戶端斷開連接 - 僅輸出

當我斷開客戶端時,服務器直到下次嘗試寫入客戶端時纔會意識到這一點。因此,服務器可能需要30秒才能識別斷開連接。

我想要做的是每隔幾秒鐘檢查一次斷開而不必等待,但我不確定如何做到這一點,因爲a)服務器沒有從客戶端接收和b)服務器無法發送任何其他數據。請有人能夠闡明這一點嗎?謝謝。

回答

9

即使您的服務器沒有從客戶端「接收」,客戶端套接字上的非阻塞讀取會告訴您無法讀取任何內容(如您所期望的),或者客戶端已斷開連接。

如果您使用的是NIO,您可以簡單地使用非阻塞的Selector循環(使用非阻塞套接字),並且只能在30秒內寫入。如果SelectionKey可讀,並且SocketChannel上的讀數返回-1,則您知道客戶端已斷開連接。

編輯:阻塞的另一種方法是簡單地選擇30秒超時。任何客戶端斷開連接都會導致select返回,您將通過讀取設備知道哪些客戶端會斷開連接。您需要在此處做的附加事項是跟蹤選擇中被阻止的時間,以確定何時在30秒標記上進行寫入(設置下一次選擇三角洲的超時時間)。

大編輯:交談MYN下面,提供完整的例子之後:

public static void main(String[] args) throws IOException { 

    ServerSocket serverSocket = null; 
    try { 
     serverSocket = new ServerSocket(4444); 
    } catch (IOException e) { 
     System.err.println("Could not listen on port: 4444."); 
     System.exit(1); 
    } 

    Socket clientSocket = null; 
    try { 
     clientSocket = serverSocket.accept(); 
    } catch (IOException e) { 
     System.err.println("Accept failed."); 
     System.exit(1); 
    } 

    // Set a 1 second timeout on the socket 
    clientSocket.setSoTimeout(1000); 

    PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); 
    BufferedReader in = new BufferedReader(
      new InputStreamReader(
      clientSocket.getInputStream())); 

    long myNextOutputTime = System.currentTimeMillis() + 30000; 

    String inputLine = null; 
    boolean connected = true; 
    while (connected) 
    { 
     try { 
      inputLine = in.readLine(); 
      if (inputLine == null) 
      { 
       System.out.println("Client Disconnected!"); 
       connected = false; 
      } 
     } 
     catch(java.net.SocketTimeoutException e) 
     { 
      System.out.println("Timed out trying to read from socket"); 
     } 

     if (connected && (System.currentTimeMillis() - myNextOutputTime > 0)) 
     { 
      out.println("My Message to the client"); 
      myNextOutputTime += 30000; 
     } 


    } 

    out.close(); 
    in.close(); 
    clientSocket.close(); 
    serverSocket.close(); 
} 

這裏值得注意的是,PrintWriter真正感動你遠離實際的插座上,你不會在寫入時捕獲套接字斷開連接(它永遠不會拋出異常,您必須使用checkError()手動檢查它)您可以更改爲使用BufferedWriter(需要使用flush()推送輸出)並像處理它一樣處理它,以便捕獲迪斯科在寫。

+0

+1或者,換句話說,你必須輪詢,說一秒鐘,一個非阻塞閱讀。 – 2011-03-10 17:47:44

+0

謝謝Brian。我沒有使用NIO,是否仍然可以使用非阻塞式讀取?我也嘗試過使用'setSoTimeout()',但是當我強迫客戶端關閉時,這似乎不起作用,這是我的測試之一。 – Myn 2011-03-11 09:59:29

+0

是否有你不使用NIO的原因?它自2002年以來一直存在,真正讓你的問題變得微不足道。我很久沒有想到我們在NIO之前如何做到這一點;查看它之前沒有一個真正的非阻塞讀取或一個'select()'。你用'setSoTimeout()'來正確的軌道,因爲這是你擁有的唯一途徑。你應該可以在你的socket讀取之前調用它,如果它超時,它會拋出一個'InterruptedIOException'。相當混亂,但 - 如果你可以使用NIO,我可以給你一個它如何工作的例子。 – 2011-03-11 15:51:56

1

如果你正在管理多個客戶端,那麼我想你會使用非阻塞套接字(如果沒有,那麼考慮使用非阻塞)。您可以使用選擇器來監視所有連接的套接字,以檢查它們是可讀的還是可寫的,或者該套接字上是否存在錯誤。當某些客戶端斷開連接時,您的選擇器將標記該套接字並返回。 更多幫助谷歌「套接字選擇功能」

相關問題