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鏈接的通信內容,這點應當注意。