如何判斷Socket鏈接失效

如今都搞升級,本人也也使用JDK6進行開發。在開發工程中對Socket進行管理時對於這個鏈接的超時和是否失效進行研究。結果網上的資料非常讓人失望,能夠說google和百度下來,前幾頁原創不多都是抄襲。 javascript

說正經的,對於鏈接超時和失效確定會想到設置超時時間和判斷鏈接是否可用。可是設置超時時間後起做用是在調用read方法的時候,若是隻是設置了超時時間卻沒有調用read,那麼就算服務端中斷鏈接,客戶端也是沒法得知的。並且就算read異常,當前的鏈接仍然是有效的。 java

咱們來看以下代碼運行後再繼續: 服務器

服務端: 網絡

Java代碼    收藏代碼
  1. package com.service;  
  2. import java.net.*;  
  3. /** 
  4.  * @說明 從這裏啓動一個服務端監聽某個端口 
  5.  * @author 崔素強 
  6.  */  
  7. public class DstService {  
  8.     public static void main(String[] args) {  
  9.         try {             
  10.             // 啓動監聽端口 8001  
  11.             ServerSocket ss = new ServerSocket(8001);  
  12.             // 沒有鏈接這個方法就一直堵塞  
  13.             Socket s = ss.accept();  
  14.             // 將請求指定一個線程去執行  
  15.             new Thread(new DstServiceImpl(s)).start();  
  16.         } catch (Exception e) {  
  17.             e.printStackTrace();  
  18.         }  
  19.     }  
  20. }  

而後咱們來看執行類,執行類在收到鏈接5秒後中斷鏈接: app

 

Java代碼    收藏代碼
  1. package com.service;  
  2. import java.net.Socket;  
  3. /** 
  4.  * @說明 服務的具體執行類 
  5.  * @author 崔素強 
  6.  */  
  7. public class DstServiceImpl implements Runnable {  
  8.     Socket socket = null;  
  9.     public DstServiceImpl(Socket s) {  
  10.         this.socket = s;  
  11.     }  
  12.     public void run() {  
  13.         try {  
  14.             int index = 1;  
  15.             while (true) {  
  16.                 // 5秒後中斷鏈接  
  17.                 if (index > 5) {  
  18.                     socket.close();  
  19.                     System.out.println("服務端已經將鏈接關閉!");  
  20.                     break;  
  21.                 }  
  22.                 index++;  
  23.                 Thread.sleep(1 * 1000);  
  24.             }  
  25.         } catch (Exception e) {  
  26.             e.printStackTrace();  
  27.         }  
  28.     }  
  29. }  

咱們在寫一個客戶端進行實驗: socket

Java代碼    收藏代碼
  1. package com.client;  
  2. import java.net.*;  
  3. /** 
  4.  * @說明 服務的客戶端,會請求鏈接並實時打印鏈接對象的一些信息,可是不會進行流的操做 
  5.  * @author 崔素強 
  6.  */  
  7. public class DstClient {  
  8.     public static void main(String[] args) {  
  9.         try {  
  10.             Socket socket = new Socket("127.0.0.1"8001);  
  11.             socket.setKeepAlive(true);  
  12.             socket.setSoTimeout(10);  
  13.             while (true) {  
  14.                 System.out.println(socket.isBound());  
  15.                 System.out.println(socket.isClosed());  
  16.                 System.out.println(socket.isConnected());  
  17.                 System.out.println(socket.isInputShutdown());  
  18.                 System.out.println(socket.isOutputShutdown());  
  19.                 System.out.println("------------------------");  
  20.                 Thread.sleep(3 * 1000);  
  21.             }  
  22.         } catch (Exception e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.     }  
  26. }  

  

至於輸出結果,雖然服務端已經中斷鏈接,可是客戶端一直輸出下面內容: ui

Xml代碼    收藏代碼
  1. true  
  2. false  
  3. true  
  4. false  
  5. false  
  6. ------------------------  

 從鏈接對象的屬性信息來看,鏈接彷佛沒有中斷。但實際雖然內存對象可用,可是物理鏈接已經失效。因此和網上其餘抄襲來抄襲去的說法同樣,靠鏈接對象屬性來判斷鏈接的可用性是不可行的this

你們會說那就判斷調用read方法是否報錯唄。我以前有文章已經討論了關於調用網絡裏面流的一些內容,在沒有判斷這個流可用以前,咱們是不會調用read方法的,固然具體你是怎麼作的我不知道我在說個人狀況! google

讀取網絡數據流時的那個方法是這樣的: spa

Java代碼    收藏代碼
  1. public static byte[] inputStreamToByte(InputStream inStream)  
  2.         throws Exception {  
  3.     int count = 0;  
  4.     int haveCheck = 0;  
  5.     // 若是在網絡傳輸中數據沒有徹底傳遞,則方法返回0  
  6.     while (count == 0) {  
  7.         count = inStream.available();  
  8.         haveCheck++;  
  9.         if (haveCheck >= 50)  
  10.             return null;  
  11.     }  
  12.     byte[] b = new byte[count];  
  13.     inStream.read(b);  
  14.     return b;  
  15. }  

 就是說咱們不會直接調用read方法,而available方法在流沒有完整和網絡中斷時都會返回0,不會報錯。

就是說就算你設置超時時間設置保持鏈接這些東西,只要你沒有調用read的機會,你的程序就不會出問題。固然若是程序一直不調用read方法,那這個程序可真的夠扯淡的了。

其實只要在使用這個鏈接的時候判斷這個鏈接的可用性就好了,不要等着什麼超時。

判斷鏈接可用雖然網上一大片,其實就是那麼回事,手動發送心跳包。

Java代碼    收藏代碼
  1. socket.sendUrgentData(0xFF); // 發送心跳包  

 乳溝你的鏈接已經中斷,那麼這個方法就會報錯。

至於什麼是心跳包,直接上理論吧。

心跳包就是在客戶端和服務器間定時通知對方本身狀態的一個本身定義的命令字,按照必定的時間間隔發送,相似於心跳,因此叫作心跳包。 用來判斷對方(設備,進程或其它網元)是否正常運行,採用定時發送簡單的通信包,若是在指定時間段內未收到對方響應,則判斷對方已經離線。用於檢測TCP的異常斷開。基本緣由是服務器端不能有效的判斷客戶端是否在線,也就是說,服務器沒法區分客戶端是長時間在空閒,仍是已經掉線的狀況。所謂的心跳包就是客戶端定時發送簡單的信息給服務器端告訴它我還在而已。代碼就是每隔幾分鐘發送一個固定信息給服務端,服務端收到後回覆一個固定信息若是服務端幾分鐘內沒有收到客戶端信息則視客戶端斷開。 好比有些通訊軟件長時間不使用,要想知道它的狀態是在線仍是離線就須要心跳包,定時發包收包。發包方:能夠是客戶也能夠是服務端,看哪邊實現方便合理,通常是客戶端。服務器也能夠定時發心跳下去。通常來講,出於效率的考慮,是由客戶端主動向服務器端發包,而不是服務器向客戶端發。客戶端每隔一段時間發一個包,使用TCP的,用send發,使用UDP的,用sendto發,服務器收到後,就知道當前客戶端還處於「活着」的狀態,不然,若是隔必定時間未收到這樣的包,則服務器認爲客戶端已經斷開,進行相應的客戶端斷開邏輯處理!

固然不能單純理解心跳包就是往對方放鬆數據,由於心跳包是用於狀態驗證的,不是真實的數據。

咱們來看以下例子,服務端不變:

Java代碼    收藏代碼
  1. package com.client;  
  2. import java.net.*;  
  3. /** 
  4.  * @說明 服務的客戶端,會請求鏈接並實時打印鏈接對象的一些信息,可是不會進行流的操做 
  5.  * @author 崔素強 
  6.  */  
  7. public class DstClient {  
  8.     public static void main(String[] args) {  
  9.         try {  
  10.             Socket socket = new Socket("127.0.0.1"8001);  
  11.             socket.setKeepAlive(true);  
  12.             socket.setSoTimeout(10);  
  13.             while (true) {  
  14.                 socket.sendUrgentData(0xFF); // 發送心跳包  
  15.                 System.out.println("目前是正常的!");  
  16.                 Thread.sleep(3 * 1000);  
  17.             }  
  18.         } catch (Exception e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.     }  
  22. }  

 看到控制檯的輸出:

Java代碼    收藏代碼
  1. 目前是正常的!  
  2. 目前是正常的!  
  3. java.net.SocketException: Invalid argument: send  
  4.     at java.net.PlainSocketImpl.socketSendUrgentData(Native Method)  
  5.     at java.net.PlainSocketImpl.sendUrgentData(PlainSocketImpl.java:550)  
  6.     at java.net.Socket.sendUrgentData(Socket.java:928)  
  7.     at com.client.DstClient.main(DstClient.java:14)  

 那就是說,只要你的服務端斷了,調用方法就會出錯!

至於我說的他不會做爲可見的數據你能夠更改服務端代碼打印客戶端內容,你會發現服務端不會將心跳包內容展現給你!

Java代碼    收藏代碼
  1. InputStream ips = socket.getInputStream();  
  2. byte[] bt = inputStreamToByte(ips);  
  3. if(null != bt)  
  4.     System.out.println(new String(bt));  
  5. else  
  6.     System.out.println("Bt is null");  
  7. System.out.println("****************************");  

 bt會一直是Null。爲何?由於我說的是對的!

哥經過示例說問題,也許不對有紕漏,可是咱絕對不去Copy,由於咱已經看厭了Copy!

相關文章
相關標籤/搜索