开发者
WebSocket的使用
WebSocket的使用
发表于2025/08/04
1030

Websocket

1.消息推送常见方式:轮询,长轮询,SSE,Websocket

** 轮询:指定间隔时间向服务器发送HTTP请求 **** **缺点:存在延迟,服务器压力大

** **长轮询:发出异步请求,服务器接到请求后,会阻塞请求,直到有数据或者超时才返回

** ****SSE(server-sent-event)**:基于HTTP协议,前端通过一个特殊的请求(EventSource)连接到服务器。,服务器给前端打开单向通道(只能由服务器返回前端),响应返回数据流,然后实时向前端传输数据,直到通道关闭

Websocket:是一种基于TCP连接上进行全双工通信的协议

http(超文本传输协议)是规定了客户端(如浏览器)与服务器之间传输数据的格式和通信方式 websocket也是一种规定了客户端和服务器传输数据格式和通信方式的协议,都位于OSI模型的第七层应用层

TCP协议(传输控制协议)(Transmission Control Protocol)是传输层协议,位于OSI模型的第四层。

** 全双工: 允许数据在两个方向上同时传输 **** **半双工:允许数据在两个方向上传输,但是一个时间段内只允许一个方向上传输

当浏览器向服务器发送请求以切换到 WebSocket 连接时,这个过程通常称为 WebSocket 握手。这个握手过程是基于 HTTP 协议的,因为 WebSocket 需要使用 HTTP 请求来启动连接。

以下是 WebSocket 握手的基本步骤:

  1. 客户端发起请求:浏览器通过发送一个特殊的 HTTP 请求到服务器来开始 WebSocket 握手。这个请求包含了 Upgrade 头部,表明客户端希望将连接从 HTTP 升级到 WebSocket。例如:
    GET /websocket HTTP/1.1
    Host: example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Version: 13
  2. 服务器响应:如果服务器同意升级连接,它会发送一个 HTTP 响应,状态码为 101(Switching Protocols),表明它同意进行协议升级。这个响应也包含了一些额外的头部,如 UpgradeConnection,以及 Sec-WebSocket-Accept,后者是服务器对客户端 Sec-WebSocket-Key 的回应。例如:
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  3. 连接建立:一旦客户端收到服务器的 101 响应,WebSocket 连接就正式建立。之后,所有的数据传输都将通过 WebSocket 协议进行,而不是 HTTP。
  4. 数据传输:在 WebSocket 连接建立后,服务器和客户端之间将使用 WebSocket 协议进行全双工通信。这意味着双方可以同时发送和接收数据,而不受 HTTP 请求-响应模型的限制。

总结来说,虽然 WebSocket 握手开始于一个 HTTP 请求,但一旦握手完成,所有的通信都将通过 WebSocket 协议进行,而不是 HTTP。这种设计允许 WebSocket 利用现有的 HTTP 端口(通常是 80 或 443)来建立连接,从而更容易地通过防火墙和代理服务器。

基本用法:

1.添加依赖:

implementation(libs.okhttp)

2.创建WebSocket监听器继承自WebSocketListener,重写四个方法

class EchoWebSocketListener:WebSocketListener() {
    override fun onOpen(webSocket: WebSocket, response: Response) {
       Log.d("WS","连接已建立")
        webSocket.send("Hello World")
    }
​
    override fun onMessage(webSocket: WebSocket, text: String) {
       Log.d("WS","收到消息:$text")
    }
​
    override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
        webSocket.close(1000,null)
        Log.d("WS"," 错误:$reason")
    }
​
    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
       Log.d("WS","错误:${t.message}")
    }
​
}

3.创建WebSocket

class MainActivity : AppCompatActivity() {
    private lateinit var webSocket:WebSocket
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        val binding= ActivityMainBinding.inflate(layoutInflater)
             binding.btn.setOnClickListener {
            startWebSocket()
        }
​
    }
​
    private fun startWebSocket(){
        val client=OkHttpClient.Builder()
        .pingInterval(30, TimeUnit.SECONDS)//设置心跳间隔,并且用枚举类型指定单位为秒
        .build()
        val request=Request.Builder()
        .url("ws://echo.websocket.org").build()//传入服务器(自动返回发送的信息)的地址
        val listener=EchoWebSocketListener()
        webSocket=client.newWebSocket(request, listener)//发送握手请求,但是还未建立,完成握手后,会调用onOpen(),30秒后去发送第一个Ping
    }

心跳的作用

** 1.定期发送PING帧,防止中间设备误杀连接 **** 2.快速感知服务器或者是网络故障,如果 PING 未立即(毫秒级延迟)收到 PONG 响应,客户端能 秒级感知 并触发重连,而非一直等待。 **** **3.通过PING维持连接活跃,避免服务器主动关闭空闲连接。

设置心跳间隔一般小于接收Pong超时时间:(如果接收Pong超时为20s,间隔为30s)** ** t=0s:建立了连接 ** t=30s:发送第一次ping,启动超时计时器 ** **t=31s:接收到pong,超时计时器重置(到51s超时)也就是说如果31s到51s之间没有任何数据传输就会超时,而下一次发送ping是在t=60s,所有导致了超时**

  • writeTimeout:用于设置写操作的超时时间,防止发送数据时长时间阻塞。(sent)
  • callTimeout:用于远程过程调用,设置等待服务器响应的最大时间。
  • readTimeout:用于设置从连接中读取数据的超时时间,确保在一定时间内接收到数据。
  • connectTimeout:用于设置建立连接的超时时间,确保在一定时间内连接成功。

代码实现:

package com.example.websocket
​
import android.nfc.Tag
import android.os.Handler
import android.os.Looper
import android.util.Log
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import java.util.concurrent.TimeUnit
​
class EchoWebSocketListener:WebSocketListener() {
    private var webSocket: WebSocket? = null
    private val handler=Handler(Looper.getMainLooper())
    private val reconnectInterval=5000L
    private val maxReconnectAttempts=5
    private var reconnectAttempts=0
    override fun onOpen(webSocket: WebSocket, response: Response) {
        this.webSocket=webSocket
       Log.d("WS","连接已建立")
        webSocket.send("Hello World")
    }
​
    override fun onMessage(webSocket: WebSocket, text: String) {
       Log.d("WS","收到消息:$text")
    }
​
    override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
        webSocket.close(code,reason)
        when(code){
            1000->Log.d("WS","正常关闭")
            else->{Log.d("Ws", reason)
            scheduleReconnect()
            }
        }
        Log.d("WS"," 错误:$reason")
    }
​
    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
       Log.d("WS","错误:${t.message}")
        scheduleReconnect()
    }
    private fun scheduleReconnect(){
        if(reconnectAttempts>=maxReconnectAttempts){
            Log.d("Ws","重连次数以达到上限")
            return
        }
        handler.postDelayed({
           Log.d("WS","正在尝试重连...")
            webSocket?.cancel()// 取消旧连接
            val client= OkHttpClient.Builder()
                .pingInterval(30, TimeUnit.SECONDS)
                .build()
            val request=Request.Builder()
                .url("ws://echo.websocket.org")
                .build()
            val newWebSocket=client.newWebSocket(request,this)
            webSocket=newWebSocket//把新的webSocket赋给webSocket
​
        },reconnectInterval)
    }
​
}

** **private val handler=Handler(Looper.getMainLooper())

**这行代码创建了一个 **Handler 实例,并将其与主线程(UI 线程)关联起来。Looper.getMainLooper() 方法返回应用程序的主线程的 Looper 实例。通过传递这个 LooperHandler 构造函数,您确保了 Handler 将在主线程上执行其 Runnable 任务。

Handler 类用于在指定的线程上执行任务,通常是在主线程(UI 线程)上。Handler 提供了一种方式来处理线程间的通信,特别是当需要在主线程上执行任务时。

状态码

  • 1000:正常关闭(如页面导航离开)
  • 1001:服务端主动断开(如维护重启)
  • 1006:异常断开(常见于网络问题)
  • 1012:服务端不可用(如负载过高)
收藏举报
Level 1
0
帖子
0
粉丝
0
获赞