天天看點

Android Socket通訊 之 心跳消息

心跳消息

  • ​​前言​​
  • ​​正文​​
  • ​​一、狀态判斷​​
  • ​​二、心跳消息發送​​
  • ​​三、心跳消息回複​​
  • ​​四、源碼​​

前言

  不知道大家國慶節過的咋樣,有沒有學習呢?我是閑着沒事就寫點東西,本文篇幅較短,隻是實作了心跳消息的處理,下面進入正文。

正文

  先說說為什麼要增加心跳消息,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, "連接配接已關閉")
                        }
                    }
                }
            }      

這裡就可以直接做處理,我列印了日志,你可以在連接配接過程中,進行測試,這段代碼添加的位置如下圖所示:

Android Socket通訊 之 心跳消息

這個代碼對于服務端和用戶端都一樣。

二、心跳消息發送

  心跳消息需要在什麼時候發送呢?在用戶端連接配接了服務端之後,由用戶端發起心跳消息,進入​

​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()​

​函數中處理,增加代碼如下圖所示:

Android Socket通訊 之 心跳消息

最後就是處理服務端回複的服務端消息,修改​

​ClientThread​

​中的代碼如下圖所示:

Android Socket通訊 之 心跳消息

  這裡判斷一下服務端回複的内容是什麼,如果是我們想要的内容就表明這是一個心跳消息,則隻列印就行了,不需要回調的頁面上進行顯示,這裡的内容因為你可能也會在輸入框輸入,是以會存在問題。

三、心跳消息回複

  當服務端收到用戶端發送的心跳時就要回複消息給用戶端,這樣用戶端就知道服務端一直都在,服務端的代碼就相對來說簡單很多了,在​

​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 失敗")
            }
        }
    }      

  在什麼時候調用它呢?當然是在收到用戶端發送消息的時候調用,隻不過和用戶端收到服務端消息一樣,同樣需要判斷一下才行,如下圖所示:

Android Socket通訊 之 心跳消息

  代碼是不是很簡單呢?其他的地方都不需要怎麼去改動了,運作一下,讓你的用戶端連接配接這個服務端,然後看控制台的日志列印,如下圖所示:

Android Socket通訊 之 心跳消息

四、源碼

繼續閱讀