前言
雨露均沾的OkHttp—WebSocket长连接(使用篇)
雨露均沾的OkHttp—WebSocket长连接(源码篇)
上期我们熟悉了OkHttp中实现WebSocket
长连接的接入,并且可以通过OkHttp
官方的MockWebSocket
服务来模拟服务端,实现整个流程。
今天我们就来说下具体OkHttp
中是怎么实现这些功能的呢?相信看过这篇文章你也能深刻了解WebSocket
这个协议。
使用回顾
简单贴下WebSocket
使用方法,方便下面解析:
//初始化
mClient = new OkHttpClient.Builder()
.pingInterval(10, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url(mWbSocketUrl)
.build();
mWebSocket = mClient.newWebSocket(request, new WsListener());
//收到消息回调
@Override
public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
super.onMessage(webSocket, text);
Log.e(TAG,"收到消息!");
onWSDataChanged(DATE_NORMAL, text);
}
//发送消息
mWebSocket.send(message);
//主动关闭连接
mWebSocket.close(code, reason);
源码解析
WebSocket
整个流程无非三个功能:连接,接收消息,发送消息。下面我们就从这三个方面
分析下具体是怎么实现的。
连接
通过上面的代码我们得知,WebSocket
连接是通过newWebSocket
方法。直接点进去看这个方法:
override fun newWebSocket(request: Request, listener: WebSocketListener): WebSocket {
val webSocket = RealWebSocket(
taskRunner = TaskRunner.INSTANCE,
originalRequest = request,
listener = listener,
random = Random(),
pingIntervalMillis = pingIntervalMillis.toLong(),
extensions = null, // Always null for clients.
minimumDeflateSize = minWebSocketMessageToCompress
)
webSocket.connect(this)
return webSocket
}
这里做了两件事:
- 初始化
RealWebSocket
,主要是设置了一些参数(比如pingIntervalMillis
心跳包时间间隔,还有监听事件之类的) connect
方法进行WebSocket
连接
继续查看connect方法:
connect(WebSocket连接握手)
fun connect(client: OkHttpClient) {
//***
val webSocketClient = client.newBuilder()
.eventListener(EventListener.NONE)
.protocols(ONLY_HTTP1)
.build()
val request = originalRequest.newBuilder()
.header("Upgrade", "websocket")
.header("Connection", "Upgrade")
.header("Sec-WebSocket-Key", key)
.header("Sec-WebSocket-Version", "13")
.header("Sec-WebSocket-Extensions", "permessage-deflate")
.build()
call = RealCall(webSocketClient, request, forWebSocket = true)
call!!.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
//得到数据流
val streams: Streams
try {
checkUpgradeSuccess(response, exchange)
streams = exchange!!.newWebSocketStreams()
}
//***
// Process all web socket messages.
try {
val name = "$okHttpName WebSocket ${
request.url.redact()}"
initReaderAndWriter(name, streams)
listener.onOpen(this@RealWebSocket, response)
loopReader()
} catch (e: Exception) {
failWebSocket(e, null)
}
}
})
}
上一篇使用篇文章中说过,Websocket
连接需要一次Http
协议的握手,然后才能把协议升级成WebSocket
。所以这段代码就体现出这个功能了。
首先就new
了一个用来进行Http
连接的request
,其中Header
的参数就表示我要进行WebSocket
连接了,参数解析如下:
Connection:Upgrade
,表示客户端要连接升级Upgrade:websocket
, 表示客户端要升级建立Websocket连接Sec-Websocket-Key:key
, 这个key是随机生成的,服务器会通过这个参数验证该请求是否有效Sec-WebSocket-Version:13
, websocket使用的版本,一般就是13Sec-webSocket-Extension:permessage-deflate
,客户端指定的一些扩展协议,比如这里permessage-deflate
就是WebSocket
的一种压缩协议。
Header
设置好之后,就调用了call
的enqueue
方法,这个方法大家应该都很熟悉吧,OkHttp
里面对于Http
请求的异步请求就是这个方法。
至此,握手结束,服务器返回响应码101
,表示协议升级。
然后我们继续看看获取服务器响应之后又做了什么?
在发送Http
请求成功之后,onResponse
响应方法里面主要表现为四个处理逻辑:
- 将
Http
流转换成WebSocket
流,得到Streams
对象,这个流后面会转化成输入流和输出流,也就是进行发送和读取的操作流 listener.onOpen(this@RealWebSocket, response)
,回调了接口WebSocketListener
的onOpen
方法,告诉用户WebSocket
已经连接initReaderAndWriter(name, streams)
loopReader()
前两个逻辑还是比较好理解,主要是后两个方法,我们分别解析下。
首先看initReaderAndWriter
方法。
initReaderAndWriter(初始化输入流输出流)
//RealWebSocket.kt
@Throws(IOException::class)
fun initReaderAndWriter(name: String, streams: Streams) {
val extensions = this.extensions!!
synchronized(this) {
//***
//写数据,发送数据的工具类
this.writer = WebSocketWriter()
//设置心跳包事件
if (pingIntervalMillis != 0L) {
val pingIntervalNanos = MILLISECONDS.toNanos(pingIntervalMillis)
taskQueue.schedule("