心跳消息
- 前言
- 正文
- 一、状态判断
- 二、心跳消息发送
- 三、心跳消息回复
- 四、源码
前言
不知道大家国庆节过的咋样,有没有学习呢?我是闲着没事就写点东西,本文篇幅较短,只是实现了心跳消息的处理,下面进入正文。
正文
先说说为什么要增加心跳消息,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 失败")
}
}
}
在什么时候调用它呢?当然是在收到客户端发送消息的时候调用,只不过和客户端收到服务端消息一样,同样需要判断一下才行,如下图所示:
代码是不是很简单呢?其他的地方都不需要怎么去改动了,运行一下,让你的客户端连接这个服务端,然后看控制台的日志打印,如下图所示: