WebSocket
聊聊OkHttp实现WebSocket细节,包括鉴权和长连接保活及其原理! - 承香墨影 - 博客园
- 今天介绍的 WebSocket,下层和 HTTP 一样也是基于 TCP 协议,这是一种轻量级网络通信协议,也属于应用层协议。 
- 轮询的缺点也非常明显,大量空闲的时间,其实是在反复发送无效的请求,这显然是一种资源的损耗。 
- WebSocket 是真正意义上的全双工模式,也就是我们俗称的「长连接」。当完成握手连接后,客户端和服务端均可以主动的发起请求,回复响应,并且两边的传输都是相互独立的。 
- 在建立连接前,客户端还需要知道服务端的地址,WebSocket 并没有另辟蹊径,而是沿用了 HTTP 的 URL 格式,但协议标识符变成了 “ws” 或者 “wss”,分别表示明文和加密的 WebSocket 协议,这一点和 HTTP 与 HTTPS 的关系类似。 
- 以下是一些 WebSocket 的 URL 例子: 
- ws://cxmydev.com/some/path ws://cxmydev.com:8080/some/path wss://cxmydev.com:443?uid=xxx
- 而在连接建立后,WebSocket 采用二进制帧的形式传输数据,其中常用的包括用于数据传输的数据帧 MESSAGE 以及 3 个控制帧: 
- PING:主动保活的 PING 帧; 
- PONG:收到 PING 帧后回复; 
- CLOSE:主动关闭 WebSocket 连接; 
WebSocket 的特性:
- WebSocket 建立在 TCP 协议之上,对服务器端友好;
- 默认端口采用 80 或 443,握手阶段采用 HTTP 协议,不容易被防火墙屏蔽,能够通过各种 HTTP 代理服务器;
- 传输数据相比 HTTP 更轻量,少了 HTTP Header,性能开销更小,通信更高效;
- 通过 MESSAGE 帧发送数据,可以发送文本或者二进制数据,如果数据过大,会被分为多个 MESSAGE 帧发送;
- WebSocket 沿用 HTTP 的 URL,==协议标识符是 “ws” 或 “wss”==。
WebSocket之OkHttp
建立 WebSocket 连接
- 借助 OkHttp 可以很轻易的实现 WebSocket,它的 OkHttpClient 中,提供了 - newWebSocket()方法,可以直接建立一个 WebSocket 连接并完成通信。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- fun connectionWebSockt(hostName:String,port:Int){
 val httpClient = OkHttpClient.Builder()
 .pingInterval(40, TimeUnit.SECONDS) // 设置 PING 帧发送间隔
 .build()
 val webSocketUrl = "ws://${hostName}:${port}"
 val request = Request.Builder()
 .url(webSocketUrl)
 .build()
 httpClient.newWebSocket(request, object:WebSocketListener(){
 // ...
 })
 }
- 调用 - newWebSocket()后,就会开始 WebSocket 连接
- WebSocketListener 是一个抽象类,其中定义了比较多的方法,借助这些方法回调,就可以完成对 WebSocket 的所有操作。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31- var mWebSocket : WebSocket? = null
 fun connectionWebSockt(hostName:String,port:Int){
 // ...
 httpClient.newWebSocket(request, object:WebSocketListener(){
 override fun onOpen(webSocket: WebSocket, response: Response) {
 super.onOpen(webSocket, response)
 // WebSocket 连接建立
 mWebSocket = webSocket
 }
 override fun onMessage(webSocket: WebSocket, text: String) {
 super.onMessage(webSocket, text)
 // 收到服务端发送来的 String 类型消息
 }
 override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
 super.onClosing(webSocket, code, reason)
 // 收到服务端发来的 CLOSE 帧消息,准备关闭连接
 }
 override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
 super.onClosed(webSocket, code, reason)
 // WebSocket 连接关闭
 }
 override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
 super.onFailure(webSocket, t, response)
 // 出错了
 }
 })
 }
- send(text):发送 String 类型的消息; 
- send(bytes):发送二进制类型的消息; 
- close(code, reason):主动关闭 WebSocket 连接; 
Mock WebSocket
- 有时候为了方便我们测试,OkHttp 还提供了扩展的 MockWebSocket 服务,来模拟服务端。
- MockWebSocket 需要添加额外的 Gradle 引用,最好和 OkHttp 版本保持一致:
- api‘com.squareup.okhttp3:okhttp:3.9.1’- api‘com.squareup.okhttp3:mockwebserver:3.9.1’
WebSocket 保活
- WebSocket 建立的连接就是我们所谓的长连接,每个连接对于服务器而言,都是资源。但服务器倾向于在一个连接长时间没有消息往来的时候,将其关闭。而 WebSocket 的保活,实际上就是定时向服务端发送一个空消息,来保证连接不会被服务端主动断开。
- OkHttp 只需要简单的配置,就可以自动的间隔发送 PING 帧和数据。
- 在构造 OkHttpClient 的时候,通过 pingInterval()设置 PING 帧发送的时间间隔,它的默认值为 0,所以不设置不发送。
- valhttpClient = OkHttpClient.Builder() .pingInterval(
- 40, TimeUnit.SECONDS)
- // 设置 PING 帧发送间隔.build()
- 这里设置的时长,需要和服务端商议,通常建议最好设置一个小于 60s 的值。