摘要
背景:心跳通常是指客户端或服务器定期发送一个小型的、空的消息以保持连接的活动状态。它用于检测连接是否仍然有效,并防止连接由于长时间没有活动而被关闭。
技术原理:App定时发消息给服务器,服务器回消息表示连接依旧有效
日志:

实现方法
1.服务端
建立WebSocket 服务器应用程序首先,添加以下依赖项到项目的
build.gradle 文件中:
dependencies {implementation ("io.ktor:ktor-server-netty:1.6.3")implementation ("io.ktor:ktor-websockets:1.6.3")implementation ("ch.qos.logback:logback-classic:1.2.6")}
使用 Ktor 框架创建一个嵌入式 Netty 服务器。它监听本地的 8080 端口,并在
/ws 路径上处理 WebSocket 连接。
import io.ktor.application.*import io.ktor.http.cio.websocket.*import io.ktor.routing.*import io.ktor.server.engine.embeddedServerimport io.ktor.server.netty.Nettyimport io.ktor.websocket.WebSocketsimport io.ktor.websocket.webSocketimport kotlinx.coroutines.GlobalScopeimport kotlinx.coroutines.launchimport java.util.concurrent.atomic.AtomicIntegerfun main() {embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)}fun Application.module() {install(WebSockets)routing {val connections = AtomicInteger(0)val clients = mutableListOf<WebSocketSession>()webSocket("/ws") {connections.incrementAndGet()println("Client connected. Total connections: ${connections.get()}")clients.add(this)try {for (frame in incoming) {when (frame) {is Frame.Text -> {val receivedText = frame.readText()println("Received message: $receivedText")send("Server received: $receivedText")// 在收到消息后,主动向所有客户端发送一条消息GlobalScope.launch {clients.forEach { client ->client.send("Server broadcast: $receivedText")}}}else -> {}}}} catch (e: Exception) {println("WebSocket error: ${e.message}")} finally {clients.remove(this)connections.decrementAndGet()println("Client disconnected. Total connections: ${connections.get()}")}}}}
运行main函数即可,等App客户端连接即可

2.客户端
使用 Kotlin 编写的长连接示例代码,用于在 Android 应用程序中建立和保持长连接首先,添加以下依赖项到项目的
build.gradle 文件中:
implementation("org.java-websocket:Java-WebSocket:1.5.1")
创建一个
WebSocketClient 类,并实现相应的方法
package com.fadi.power.net.serviceimport android.util.Logimport com.fadi.power.net.config.Configimport com.fadi.power.net.utils.TimeUtilsimport org.java_websocket.client.WebSocketClientimport org.java_websocket.drafts.Draftimport org.java_websocket.handshake.ServerHandshakeimport java.net.URIimport java.util.concurrent.Executorsimport java.util.concurrent.ScheduledExecutorServiceimport java.util.concurrent.TimeUnitclass WebSocketClient(serverUri: URI, draft: Draft) : WebSocketClient(serverUri, draft) {companion object {const val HEARTBEAT_MESSAGE = "Heartbeat"}private var mEnableHeartbeat = falseprivate var heartbeatExecutor: ScheduledExecutorService? = nulloverride fun onOpen(handshakedata: ServerHandshake?) {// 连接成功,发送数据或执行其他操作send("Hello, Server!")Log.d(Config.TAG, "WebSocketClient onOpen: Hello, Server!")// 【不使用自带的心跳,使用自建Alarm定时】启动定时任务发送心跳消息// startHeartbeat()}override fun onClose(code: Int, reason: String?, remote: Boolean) {// 【不使用自带的心跳,使用自建Alarm定时】连接关闭,执行相应的处理逻辑// 停止心跳任务// stopHeartbeat()}override fun onMessage(message: String?) {// 接收到服务器发送的消息,执行相应的处理逻辑message?.let {Log.d(Config.TAG, "WebSocketClient onMessage: Received message: $it")}}override fun onError(ex: Exception?) {// 连接出现错误,执行相应的错误处理逻辑ex?.let {Log.d(Config.TAG, "WebSocketClient onError: WebSocket error: ${it.message}")}}private fun startHeartbeat() {heartbeatExecutor = Executors.newSingleThreadScheduledExecutor()heartbeatExecutor?.scheduleAtFixedRate({ sendHeartbeat() },0, // 初始延迟为01, // 间隔为1分钟TimeUnit.MINUTES)}private fun stopHeartbeat() {heartbeatExecutor?.shutdownNow()heartbeatExecutor = null}fun sendHeartbeat() {if (!mEnableHeartbeat) {return}val timestamp = System.currentTimeMillis()val heartbeatMessage = "$HEARTBEAT_MESSAGE ${TimeUtils.timeStamp2Date(timestamp)}"send(heartbeatMessage)Log.d(Config.TAG, "WebSocketClient sendHeartbeat: Sending heartbeat message: $heartbeatMessage")}fun setEnableHeartbeat(enable: Boolean) {mEnableHeartbeat = enable}fun isHeartbeat(): Boolean {return mEnableHeartbeat}}
应用启动时,它将连接到指定的 WebSocket 服务器,并在连接成功后发送一条消息
// 服务器ip: 把电脑当做服务器, ipconfig获取const val SERVER_IP = "10.170.16.162"const val SERVER_DOWNLOAD_PORT = "8000"const val DOWN_LOAD_FILE_NAME = "test.apk"//http://10.170.16.162:8000/test.apk"const val DOWN_LOAD_URL = "http:" + SERVER_IP + ":" + SERVER_DOWNLOAD_PORT + "/" + DOWN_LOAD_FILE_NAME// 加密 wss://192.168.0.100:8080/ws// 非加密 ws://10.170.16.162:8080/wsconst val SERVER_PUSH_PORT = "8080"const val PUSH_URL = "ws://" + SERVER_IP + ":" + SERVER_PUSH_PORT + "/ws"初始化val serverUri = URI(Config.PUSH_URL)val draft = Draft_6455() // 使用 WebSocket 协议版本 13mWebSocketClient = WebSocketClient(serverUri, draft)mWebSocketClient.connect()private fun handleHeadBeatEvent(scene: String, checked: Boolean) {// 心跳场景的处理逻辑if (checked == true) {// 开启心跳Log.d(Config.TAG, "开启心跳")enableOneMinuteAlarm()mWebSocketClient.setEnableHeartbeat(true)mAlarmNetWorkNotification.showNotification(scene)} else {// 关闭心跳Log.d(Config.TAG, "关闭心跳")disableOneMinuteAlarm()mWebSocketClient.setEnableHeartbeat(false)mAlarmNetWorkNotification.hideNotification(scene)}}
3.运行结果
支持设定心跳间隔,调试阶段定了了1分钟一次和服务端的心跳长连接
3.1 客户端打印

3.2 服务端打印














![[paddle]paddleseg快速开始](https://img-blog.csdnimg.cn/img_convert/3baa205345e943971ce0a31b0a6d1b8c.png)





