比isConnected()更靠譜的的獲取socket實時連接狀態!


看到這個標題,預計非常多人會說用socket.isConnected() 者socket.isClosed()等方法來推斷即可了,但其實這些方法都是訪問socket在內存駐留的狀態,當socket和server端建立鏈接后,即使socket鏈接斷掉了,調用上面的方法返回的仍然是鏈接時的狀態,而不是socket的實時鏈接狀態。以下給出樣例證明這一點。

  server端:

  package com.csc.server;
  import java.net.*;
  /**
  * @description 從這里啟動一個服務端監聽某個port
  * @author csc
  */
  public class DstService {
  public static void main(String[] args) {
  try {
  // 啟動監聽port 30000
  ServerSocket ss = new ServerSocket(30000);
  // 沒有連接這種方法就一直阻塞
  Socket s = ss.accept();
  // 將請求指定一個線程去運行
  new Thread(new DstServiceImpl(s)).start();
  } catch (Exception e) {
  e.printStackTrace();
  }
  }
  }
  這里我設置了啟動新線程來管理建立的每個socket鏈接,此處我們設置收到鏈接后10秒端來鏈接。代碼例如以下:
  package com.csc.server;
  import java.net.Socket;
  /**
  * @description 服務的啟動的線程類
  * @author csc
  */
  public class DstServiceImpl implements Runnable {
  Socket socket = null;
  public DstServiceImpl(Socket s) {
  this.socket = s;
  }
  public void run() {
  try {
  int index = 1;
  while (true) {
  // 5秒后中斷連接
  if (index > 10) {
  socket.close();
  System.out.println("服務端已經關閉鏈接!");
  break;
  }
  index++;
  Thread.sleep(1 * 1000);//程序睡眠1秒鍾
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
  }
  }
  以上是服務端代碼,以下寫一個client代碼來測試:
  package com.csc.client;
  import java.net.*;
  /**
  * @description client打印鏈接狀態
  * @author csc
  */
  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) {
  System.out.println(socket.isBound());
  System.out.println(socket.isClosed());
  System.out.println(socket.isConnected());
  System.out.println(socket.isInputShutdown());
  System.out.println(socket.isOutputShutdown());
  System.out.println("------------我是切割線------------");
  Thread.sleep(3 * 1000);
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
  }
  }
  先執行服務端代碼,再執行client代碼,我們會在client代碼的控制台看到例如以下信息:
  true
  false
  true
  false
  false
  ------------我是切割線------------
  從連接對象的屬性信息來看,連接是沒有中斷,但實際鏈接已經在服務端建立鏈接10秒后斷開了。

這說明了上述幾個方法是不能實時推斷出socket的鏈接狀態,僅僅是socket駐留在內存的狀態。事實上。此時假設調用流去讀取信息的話。就會出現異常。


  事實上,想要推斷socket是否仍是鏈接狀態。僅僅要發一個心跳包即可了,例如以下一句代碼:

  socket.sendUrgentData(0xFF); // 發送心跳包
  關於心跳包的理論能夠去google一下。我給出點參考:心跳包就是在client和server間定時通知對方自己狀態的一個自定義的命令字,依照一定的時間間隔發送,類似於心跳。所以叫做心跳包。 用來推斷對方(設備。進程或其他網元)是否正常執行。採用定時發送簡單的通訊包。假設在指定時間段內未收到對方響應,則推斷對方已經離線。用於檢測TCP的異常斷開。基本原因是server端不能有效的推斷client是否在線。也就是說。server無法區分client是長時間在空暇,還是已經掉線的情況。所謂的心跳包就是client定時發送簡單的信息給server端告訴它我還在而已。代碼就是每隔幾分鍾發送一個固定信息給服務端。服務端收到后回復一個固定信息假設服務端幾分鍾內沒有收到client信息則視client斷開。 比方有些通信軟件長時間不使用,要想知道它的狀態是在線還是離線就須要心跳包,定時發包收包。發包方:能夠是客戶也能夠是服務端,看哪邊實現方便合理,通常是client。

server也能夠定時發心跳下去。一般來說,出於效率的考慮。是由client主動向server端發包,而不是server向client發。client每隔一段時間發一個包,使用TCP的,用send發,使用UDP的,用sendto發,server收到后,就知道當前client還處於“活着”的狀態,否則。假設隔一定時間未收到這種包,則server覺得client已經斷開,進行對應的client斷開邏輯處理。
  既然找到了方法,我們就在測試一下。服務端代碼無需修改,client代碼例如以下:

  package com.csc.client;
  import java.net.*;
  /**
  * @description client打印鏈接狀態
  * @author csc
  */
  public class DstClient {
  public static void main(String[] args) {
  try {
  Socket socket = new Socket("127.0.0.1", 30000);
  socket.setKeepAlive(true);
  socket.setSoTimeout(10);
  while (true) {
  socket.sendUrgentData(0xFF); // 發送心跳包
  System.out.println("眼下是處於鏈接狀態。");
  Thread.sleep(3 * 1000);//線程睡眠3秒
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
  }
  }
  又一次執行client程序,看到控制台打印例如以下信息:
  眼下是處於鏈接狀態!


  眼下是處於鏈接狀態!
  眼下是處於鏈接狀態!


  java.net.SocketException: Invalid argument: sendat java.net.PlainSocketImpl.socketSendUrgentData(Native Method)at java.net.PlainSocketImpl.sendUrgentData(PlainSocketImpl.java:550)at java.net.Socket.sendUrgentData(Socket.java:928)at com.client.DstClient.main(DstClient.java:14) 這說明當運行“socket.sendUrgentData(0xFF);”這個語句時,socket鏈接斷開了,運行失敗拋出了異常。
  另外注意,心跳包僅僅是用來檢測socket的鏈接狀態。並不會作為socket鏈接的通信內容,這點應當注意。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM