OkHttp原理分析(八)

386 阅读1分钟

实现WebSocket通信客户端原理

override fun newWebSocket(request: Request, listener: WebSocketListener): WebSocket {
  val webSocket = RealWebSocket(request, listener, Random(), pingIntervalMillis.toLong())
  // 使用HTTP协议连接
  webSocket.connect(this)
  return 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")
    .build()
  call = RealCall.newRealCall(webSocketClient, request, forWebSocket = true)
  call!!.enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
      val exchange = response.exchange
      val streams: Streams
      try {
        // 检查WebSocket是否创建成功
        checkUpgradeSuccess(response, exchange)
        // 创建WebSocket流 
        streams = exchange!!.newWebSocketStreams()
      } catch (e: IOException) {
        exchange?.webSocketUpgradeFailed()
        failWebSocket(e, response)
        response.closeQuietly()
        return
      }

      // 处理所有Web套接字消息。
      try {
        val name = "OkHttp WebSocket ${request.url.redact()}"
        // 初始化
        initReaderAndWriter(name, streams)
        // 实现WebSocketListener开始操作
        listener.onOpen(this@RealWebSocket, response)
        // 异步循环读取响应
        loopReader()
      } catch (e: Exception) {
        failWebSocket(e, null)
      }
    }
    override fun onFailure(call: Call, e: IOException) {
      failWebSocket(e, null)
    }
  })
}
fun initReaderAndWriter(name: String, streams: Streams) {
  synchronized(this) {
    this.streams = streams
    this.writer = WebSocketWriter(streams.client, streams.sink, random)
    this.executor = ScheduledThreadPoolExecutor(1, threadFactory(name, false))
    if (pingIntervalMillis != 0L) {
      executor!!.scheduleAtFixedRate(
        PingRunnable(), pingIntervalMillis, pingIntervalMillis, MILLISECONDS)
    }
    if (messageAndCloseQueue.isNotEmpty()) {
      runWriter() // 连接后发送异步消息
    }
  }

  reader = WebSocketReader(streams.client, streams.source, this)
}
fun loopReader() {
  while (receivedCloseCode == -1) {
    // 多次调用onRead*方法
    reader!!.processNextFrame()
  }
}
fun processNextFrame() {
  readHeader()
  if (isControlFrame) {
    readControlFrame()
  } else {
    // 执行WebSocketListener的onRead方法
    readMessageFrame()
  }
}
@Synchronized private fun send(data: ByteString, formatOpcode: Int): Boolean {
  // 在我们失败或排队关闭帧后不要发送新帧。
  if (failed || enqueuedClose) return false

  // 如果此帧溢出缓冲区,请拒绝它并关闭Web套接字。
  if (queueSize + data.size > MAX_QUEUE_SIZE) {
    close(CLOSE_CLIENT_GOING_AWAY, null)
    return false
  }

  // 将消息帧排入队列。
  queueSize += data.size.toLong()
  messageAndCloseQueue.add(Message(formatOpcode, data))
  runWriter() // // 连接后发送异步消息
  return true
}
private fun runWriter() {
  assert(Thread.holdsLock(this))
  // 只有一个线程的循环任务线程池
  executor?.execute(writerRunnable)
}

总结

OkHttp的WebSocket实现是将请求和响应分别放在了一个异步线程中,下一个请求不需要等待上一个响应返回就可以发送,请求是通过只有一个线程的循环任务线程池发送的,响应在Http异步请求的线程中获取。