1. 程式人生 > >如何判斷socket是否連線,異常斷開拔出網線

如何判斷socket是否連線,異常斷開拔出網線

                       

轉載請標明出處:http://blog.csdn.net/xx326664162/article/details/48522381   文章出自:薛瑄的部落格

你也可以檢視我的其他同類文章,也會讓你有一定的收貨!

  • setSoTimeout
 public void setSoTimeout
(int timeout)            throws SocketException
  • 1
  • 2

啟用/禁用帶有指定超時值的 SO_TIMEOUT,以毫秒為單位。將此選項設為非零的超時值時,在與此 Socket 關聯的 InputStream 上呼叫 read() 將只阻塞此時間長度。如果超過超時值,將引發 java.net.SocketTimeoutException,雖然Socket  仍舊有效。選項必須在進入阻塞操作前被啟用才能生效。超時值必須是 > 0 的數。超時值為 0 被解釋為無窮大超時值。 引數:
   timeout - 指定的以毫秒為單位的超時值。 丟擲: SocketException -  如果底層協議出現錯誤,例如 TCP 錯誤。

設定超時時間保持連線,只要沒有呼叫read()函式,即使超時程式就不會丟擲異常

  • connect
public void connect(SocketAddress endpoint,                      int timeout)               throws
IOException
  • 1
  • 2
  • 3

將此套接字連線到伺服器,並指定一個超時值。超時值零被解釋為無限超時。在建立連線或者發生錯誤之前,連線一直處於阻塞狀態。
 引數:   endpoint - SocketAddress timeout - 要使用的超時值(以毫秒為單位)。 丟擲: IOException -

  • 如果在連線期間發生錯誤 SocketTimeoutException
  • 如果在連線之前超時期滿 IllegalBlockingModeException
  • 如果此套接字具有關聯的通道並且該通道處於非阻塞模式 IllegalArgumentException
  • 如果端點為 null 或者此套接字不支援 SocketAddress 子類

  • 心跳包就是在客戶端和伺服器間定時通知對方自己狀態的一個自己定義的命令字,按照一定的時間間隔傳送,類似於心跳,所以叫做心跳包

    • 用來判斷對方(裝置,程序或其它網元)是否正常執行,檢測TCP的異常斷開。

    • 基本原因是伺服器端不能有效的判斷客戶端是否線上,也就是說,伺服器無法區分客戶端是長時間在空閒,還是已經掉線的情況。

    • 所謂的心跳包就是客戶端定時傳送簡單的資訊給伺服器端告訴它我還在。程式碼就是每隔幾分鐘傳送一個固定資訊給服務端,服務端收到後回覆一個固定資訊如果服務端幾分鐘內沒有收到客戶端資訊則視客戶端斷開。

    • 發包方:可以是客戶也可以是服務端,看哪邊實現方便合理,一般是客戶端。伺服器也可以定時發心跳下去。一般來說,出於效率的考慮,是由客戶端主動向伺服器端發包,而不是伺服器向客戶端發。客戶端每隔一段時間發一個包,使用TCP的,用send發,使用UDP的,用sendto發,伺服器收到後,就知道當前客戶端還處於“活著”的狀態,否則,如果隔一定時間未收到這樣的包,則伺服器認為客戶端已經斷開,進行相應的客戶端斷開邏輯處理!
    • 當然不能單純理解心跳包就是往對方放鬆資料,因為心跳包是用於狀態驗證的,不是真實的資料。
  • 正常斷開連線,該函式會丟擲異常

  • 無法判斷伺服器網路異常斷開,如:拔掉網線。

    如果使用sendUrgentData()函式,即使在直接拔掉網線的情況下,該函式依然不會丟擲異常資訊,這個函式只是傳送緊急資料,

socket.sendUrgentData(0xFF); // 傳送心跳包
   
  • 1

服務端:

package com.service;  import java.net.*;  /**  - @說明 從這裡啟動一個服務端監聽某個埠  */  public class DstService {      public static void main(String[] args) {          try {                         // 啟動監聽埠 8001              ServerSocket ss = new ServerSocket(8001);              // 沒有連線這個方法就一直堵塞              Socket s = ss.accept();              // 將請求指定一個執行緒去執行              new Thread(new DstServiceImpl(s)).start();          } catch (Exception e) {              e.printStackTrace();          }      }  } 
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

服務端執行類:

package com.service;  import java.net.Socket;  /**  * @說明 服務的具體執行類  */  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 > 5) {                      socket.close();                      System.out.println("服務端已經將連線關閉!");                      break;                  }                  index++;                  Thread.sleep(1 * 1000);              }          } catch (Exception e) {              e.printStackTrace();          }      }  } 
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

測試客戶端一:

package com.client;  import java.net.*;  /**  * @說明 服務的客戶端,會請求連線並實時列印連線物件的一些資訊,但是不會進行流的操作 */  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();          }      }  } 
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

五秒後,服務端斷開,但客戶端還是一直輸出一下內容:

true  false  true  false  false  ------------------------  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在判斷伺服器是否斷開時,首先想到socket類的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等,但經過試驗並檢視相關文件,這些方法都是本地端的狀態,無法判斷遠端是否已經斷開連線。

測試客戶端二:

package com.client;  import java.net.*;  /**  * @服務的客戶端,會請求連線並實時列印連線物件的一些資訊,但是不會進行流的操作  */  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();          }      }  }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

這個連結中說到,sendUrgentData(0xFF)和in.readLine(); 這兩個方法都只能判斷出伺服器關閉時斷線的情況(正常斷開),不能判斷出直接拔掉網線的操作。

注意以上程式為單執行緒,如果斷開連線,客戶端再進行連線,你會發現,在服務端不能打印出“服務端已經將連線關閉!”詳細原因請參加這篇部落格

參考:http://cuisuqiang.iteye.com/blog/1453632
http://www.apkbus.com/android-142289-1-1.html

 

關注我的公眾號,輕鬆瞭解和學習更多技術
  這裡寫圖片描述

           

再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://blog.csdn.net/jiangjunshow