心跳消息
- 前言
- 正文
- 一、狀态判斷
- 二、心跳消息發送
- 三、心跳消息回複
- 四、源碼
前言
不知道大家國慶節過的咋樣,有沒有學習呢?我是閑着沒事就寫點東西,本文篇幅較短,隻是實作了心跳消息的處理,下面進入正文。
正文
先說說為什麼要增加心跳消息,Socket在連接配接過程中會發生很多的意外,比如網絡問題、連接配接不上、ip位址不對、異常斷連,而心跳消息說白了就是通過指定時間去收發消息,通過收發消息就能知道連接配接雙方處于什麼狀态。知道是什麼狀态就知道該怎麼處理。
一、狀态判斷
在開始寫之前我們需要先知道狀态有哪些,直接在
IOException
中進行處理。
catch (e: IOException) {
e.printStackTrace()
when (e) {
is SocketTimeoutException -> {
Log.e(TAG, "連接配接逾時,正在重連")
}
is NoRouteToHostException -> {
Log.e(TAG, "該位址不存在,請檢查")
}
is ConnectException -> {
Log.e(TAG, "連接配接異常或被拒絕,請檢查")
}
is SocketException -> {
when (e.message) {
"Already connected" -> Log.e(TAG, "連接配接異常或被拒絕,請檢查")
"Socket closed" -> Log.e(TAG, "連接配接已關閉")
}
}
}
}
這裡就可以直接做處理,我列印了日志,你可以在連接配接過程中,進行測試,這段代碼添加的位置如下圖所示:
這個代碼對于服務端和用戶端都一樣。
二、心跳消息發送
心跳消息需要在什麼時候發送呢?在用戶端連接配接了服務端之後,由用戶端發起心跳消息,進入
SocketClient
,增加如下代碼:
private const val HEART_SPACETIME = 3 * 1000
private val mHandler: Handler = Handler()
這裡定義了一個心跳間隔時間,也就是說,3秒發送一次心跳,下面寫一個發送心跳的方法。
private val mHeartRunnable = Runnable { sendHeartbeat() }
/**
* 發送心跳消息
*/
private fun sendHeartbeat() {
if (clientThreadPool == null) {
clientThreadPool = Executors.newSingleThreadExecutor()
}
val msg = "洞幺洞幺,呼叫洞拐,聽到請回答,聽到請回答,Over!"
clientThreadPool?.execute {
if (socket == null) {
mCallback.otherMsg("用戶端還未連接配接")
return@execute
}
if (socket!!.isClosed) {
mCallback.otherMsg("Socket已關閉")
return@execute
}
outputStream = socket?.getOutputStream()
try {
outputStream?.write(msg.toByteArray())
outputStream?.flush()
//發送成功以後,重建立立一個心跳消息
mHandler.postDelayed(mHeartRunnable, HEART_SPACETIME.toLong())
Log.i(TAG, msg)
} catch (e: IOException) {
e.printStackTrace()
mCallback.otherMsg("向服務端發送消息: $msg 失敗")
}
}
}
心跳消息中我定義了消息的内容,服務端收到這個内容就表示為心跳消息,這個消息内容還是有待商榷的,先實作功能就行,在這裡發送消息之後,進行延時發送下一次心跳消息,這裡列印了發送的心跳消息了,因為是連接配接到服務端之後就開始發送,是以還需要在
connectServer()
函數中處理,增加代碼如下圖所示:
最後就是處理服務端回複的服務端消息,修改
ClientThread
中的代碼如下圖所示:
這裡判斷一下服務端回複的内容是什麼,如果是我們想要的内容就表明這是一個心跳消息,則隻列印就行了,不需要回調的頁面上進行顯示,這裡的内容因為你可能也會在輸入框輸入,是以會存在問題。
三、心跳消息回複
當服務端收到用戶端發送的心跳時就要回複消息給用戶端,這樣用戶端就知道服務端一直都在,服務端的代碼就相對來說簡單很多了,在
SocketServer
中增加一個函數,代碼如下:
fun replyHeartbeat() {
if (serverThreadPool == null) {
serverThreadPool = Executors.newCachedThreadPool()
}
val msg = "洞拐收到,洞拐收到,Over!"
serverThreadPool?.execute {
if (socket == null) {
mCallback.otherMsg("用戶端還未連接配接")
return@execute
}
if (socket!!.isClosed) {
mCallback.otherMsg("Socket已關閉")
return@execute
}
outputStream = socket!!.getOutputStream()
try {
outputStream.write(msg.toByteArray())
outputStream.flush()
} catch (e: IOException) {
e.printStackTrace()
mCallback.otherMsg("向用戶端發送消息: $msg 失敗")
}
}
}
在什麼時候調用它呢?當然是在收到用戶端發送消息的時候調用,隻不過和用戶端收到服務端消息一樣,同樣需要判斷一下才行,如下圖所示:
代碼是不是很簡單呢?其他的地方都不需要怎麼去改動了,運作一下,讓你的用戶端連接配接這個服務端,然後看控制台的日志列印,如下圖所示: