如何判断Socket连接失效
在開發工程中對Socket進行管理時對于這個連接的超時和是否失效進行研究。
對于連接超時和失效肯定會想到設置超時時間和判斷連接是否可用。但是設置超時時間后起作用是在調用read方法的時候,如果只是設置了超時時間卻沒有調用read,那么就算服務端中斷連接,客戶端也是無法得知的。而且就算read異常,當前的連接仍然是有效的。
我們來看如下代碼運行后再繼續:
服務端:
package com.service; import java.net.*; /*** @說明 從這里啟動一個服務端監聽某個端口*/ public class DstService {public static void main(String[] args) {try { // 啟動監聽端口 8001ServerSocket ss = new ServerSocket(8001);// 沒有連接這個方法就一直堵塞Socket s = ss.accept();// 將請求指定一個線程去執行new Thread(new DstServiceImpl(s)).start();} catch (Exception e) {e.printStackTrace();}} }
 然后我們來看執行類,執行類在收到連接5秒后中斷連接:
 我們在寫一個客戶端進行實驗:
 至于輸出結果,雖然服務端已經中斷連接,但是客戶端一直輸出下面內容:
true
 false
 true
 false
 false
 ------------------------
從連接對象的屬性信息來看,連接似乎沒有中斷。但實際雖然內存對象可用,但是物理連接已經失效。所以和網上其他抄襲來抄襲去的說法一樣,靠連接對象屬性來判斷連接的可用性是不可行的。
大家會說那就判斷調用read方法是否報錯唄。關于調用網絡里面流的一些內容,在沒有判斷這個流可用之前,我們是不會調用read方法的。
讀取網絡數據流時的那個方法是這樣的:
public static byte[] inputStreamToByte(InputStream inStream)throws Exception {int count = 0;int haveCheck = 0;// 如果在網絡傳輸中數據沒有完全傳遞,則方法返回0while (count == 0) {count = inStream.available();haveCheck++;if (haveCheck >= 50)return null;}byte[] b = new byte[count];inStream.read(b);return b; }
 ?就是說我們不會直接調用read方法,而available方法在流沒有完整和網絡中斷時都會返回0,不會報錯。
就是說就算你設置超時時間設置保持連接這些東西,只要你沒有調用read的機會,你的程序就不會出問題。當然如果程序一直不調用read方法,那這個程序可真的夠扯淡的了。
其實只要在使用這個連接的時候判斷這個連接的可用性就行了,不要等著什么超時。
判斷連接可用雖然網上一大片,其實就是那么回事,手動發送心跳包。
socket.sendUrgentData(0xFF); // 發送心跳包
如果你的連接已經中斷,那么這個方法就會報錯。
至于什么是心跳包,直接上理論吧。
心跳包就是在客戶端和服務器間定時通知對方自己狀態的一個自己定義的命令字,按照一定的時間間隔發送,類似于心跳,所以叫做心跳包。 用來判斷對方(設備,進程或其它網元)是否正常運行,采用定時發送簡單的通訊包,如果在指定時間段內未收到對方響應,則判斷對方已經離線。用于檢測TCP的異常斷開。基本原因是服務器端不能有效的判斷客戶端是否在線,也就是說,服務器無法區分客戶端是長時間在空閑,還是已經掉線的情況。所謂的心跳包就是客戶端定時發送簡單的信息給服務器端告訴它我還在而已。代碼就是每隔幾分鐘發送一個固定信息給服務端,服務端收到后回復一個固定信息如果服務端幾分鐘內沒有收到客戶端信息則視客戶端斷開。 比如有些通信軟件長時間不使用,要想知道它的狀態是在線還是離線就需要心跳包,定時發包收包。發包方:可以是客戶也可以是服務端,看哪邊實現方便合理,一般是客戶端。服務器也可以定時發心跳下去。一般來說,出于效率的考慮,是由客戶端主動向服務器端發包,而不是服務器向客戶端發。客戶端每隔一段時間發一個包,使用TCP的,用send發,使用UDP的,用sendto發,服務器收到后,就知道當前客戶端還處于“活著”的狀態,否則,如果隔一定時間未收到這樣的包,則服務器認為客戶端已經斷開,進行相應的客戶端斷開邏輯處理!
當然不能單純理解心跳包就是往對方放松數據,因為心跳包是用于狀態驗證的,不是真實的數據。
我們來看如下例子,服務端不變:
package com.client; import java.net.*; /*** @說明 服務的客戶端,會請求連接并實時打印連接對象的一些信息,但是不會進行流的操作* @author 崔素強*/ public class DstClient {public static void main(String[] args) {try {Socket socket = new Socket("127.0.0.1", 8001);socket.setKeepAlive(true);socket.setSoTimeout(10);while (true) {socket.sendUrgentData(0xFF); // 發送心跳包System.out.println("目前是正常的!");Thread.sleep(3 * 1000);}} catch (Exception e) {e.printStackTrace();}} }
 ?看到控制臺的輸出:
目前是正常的!
 目前是正常的!
 
 那就是說,只要你的服務端斷了,調用方法就會出錯!
它不會作為可見的數據,你可以更改服務端代碼打印客戶端內容,你會發現服務端不會將心跳包內容展示給你!
InputStream ips = socket.getInputStream(); byte[] bt = inputStreamToByte(ips); if(null != bt)System.out.println(new String(bt)); elseSystem.out.println("Bt is null"); System.out.println("****************************");
 bt會一直是Null
總結
以上是生活随笔為你收集整理的如何判断Socket连接失效的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Java socket中isClose(
 - 下一篇: IT人分类,你属于哪个级别?