OkHttp3笔记----CallServerInterceptor

    在上一篇ConnectInterceptor,介绍了Http的连接过程。本篇将介绍Http如何发送和接收数据,在OkHttp中,CallServerInterceptor用来发送和接收数据。接下来以Http2.0,介绍数据发送接收的大体过程,在Http2.0协议中,数据会被封装成数据帧进行发送。

CallServerInterceptor概览:

    Http2Connection:表示了一个Http连接,包含了Http2Writer,其负责将数据帧发送给服务器,Http2Reader其负责接收服务发送的数据。

    Http2Stream:每一个Http2Stream都与一个Http请求相关联,其包含一个streamId,在发送和接收数据时,数据帧都会含有此id,用来区别不同的Http请求。

    FramingSink,它将要发送的数据以Http2.0数据帧的形式交给Http2Writer,具体过程在2、发送RequestBody中介绍。

    FramingSource,它接收Http2Reader传递的数据帧,并交付Response,具体过程在4、接收ResponseBody中介绍。

    CallServerInterceptor实现了Http的通信过程,其主要分为如下4步:

        1、发送RequestHeader;2、发送RequestBody;3接收ResponseHeader;4、接收ResponseBody。

    接下来从代码角度分析以上4个步骤(以Http2.0为例)

代码分析:

    1、发送RequestHeader

     CallServerInterceptor的intercept代码如下:

override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.exchange!!
    val request = realChain.request
    val requestBody = request.body
    val sentRequestMillis = System.currentTimeMillis()
    //发送请求头
    exchange.writeRequestHeaders(request)

    var invokeStartEvent = true
    var responseBuilder: Response.Builder? = null
    ......
    return response
  }

    OkHttp会调用writeRequestHeaders方法,发送请求头,其代码如下:

fun writeRequestHeaders(request: Request) {
    try {
      eventListener.requestHeadersStart(call)
      codec.writeRequestHeaders(request)
      eventListener.requestHeadersEnd(call, request)
    } catch (e: IOException) {
      eventListener.requestFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

    此步会调用Http2ExchangeCodec的writeRequestHeaders方法,其代码如下:

override fun writeRequestHeaders(request: Request) {
    if (stream != null) return

    val hasRequestBody = request.body != null
    val requestHeaders = http2HeadersList(request)
    //此方法作用:1、新建Http2Stream,其负责发送和接收服务器的数据
    //2、发送请求头
    stream = http2Connection.newStream(requestHeaders, hasRequestBody)
    // We may have been asked to cancel while creating the new stream and sending the request
    // headers, but there was still no stream to close.
    if (canceled) {
      stream!!.closeLater(ErrorCode.CANCEL)
      throw IOException("Canceled")
    }
    stream!!.readTimeout().timeout(chain.readTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)
    stream!!.writeTimeout().timeout(chain.writeTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)
  }

    接下来会调用Http2Connection的newStream方法创建一个Http2Stream,以发送和接收数据,并发送请求头。newStream方法的代码如下:

fun newStream(
    requestHeaders: List<Header>,
    out: Boolean
  ): Http2Stream {
    //调用重载方法,注意此方法的第一个参数为0
    return newStream(0, requestHeaders, out)
  }

  @Throws(IOException::class)
  private fun newStream(
    associatedStreamId: Int,
    requestHeaders: List<Header>,
    out: Boolean
  ): Http2Stream {
    val outFinished = !out
    val inFinished = false
    val flushHeaders: Boolean
    val stream: Http2Stream
    val streamId: Int
    //此同步块使用writer作为锁,保证了某一时刻,只有一个线程能发送请求头。请求头会被封装成数据帧发送,而发送数据帧不是一个“原子”操作,其有2步,第一步先发送帧头,第二步发送帧体。因此需要锁来保证这2步可以一起执行。
    //Http2.0存在复用连接的情况,即某一时刻,可能有多个线程发送请求头,因此,在此处需要确保线程安全。
    //同时确保streamId的唯一性
    synchronized(writer) {
      synchronized(this) {
        if (nextStreamId > Int.MAX_VALUE / 2) {
          shutdown(REFUSED_STREAM)
        }
        if (isShutdown) {
          throw ConnectionShutdownException()
        }
        //区别不同的Http请求,因为Http2.0会复用连接,即使用同一个连接,发送多个Http请求的数据
        streamId = nextStreamId
        nextStreamId += 2
        //创建Http2Stream
        stream = Http2Stream(streamId, this, outFinished, inFinished, null)
        flushHeaders = !out ||
            writeBytesTotal >= writeBytesMaximum ||
            stream.writeBytesTotal >= stream.writeBytesMaximum
        if (stream.isOpen) {
          //记录创建的stream
          streams[streamId] = stream
        }
      }
      //此形参的值为0,验证为true,发送请求头
      if (associatedStreamId == 0) {
        writer.headers(outFinished, streamId, requestHeaders)
      } else {
        require(!client) { "client streams shouldn't have associated stream IDs" }
        // HTTP/2 has a PUSH_PROMISE frame.
        writer.pushPromise(associatedStreamId, streamId, requestHeaders)
      }
    }

    if (flushHeaders) {
      writer.flush()
    }

    return stream
  }

    在Http2Stream创建完成后,接下来会调用header方法,发送请求头,其代码如下:

fun headers(
    outFinished: Boolean,
    streamId: Int,
    headerBlock: List<Header>
  ) {
    if (closed) throw IOException("closed")
    //对请求头进行压缩,以减少发送数据的大小
    hpackWriter.writeHeaders(headerBlock)

    val byteCount = hpackBuffer.size
    val length = minOf(maxFrameSize.toLong(), byteCount)
    var flags = if (byteCount == length) FLAG_END_HEADERS else 0
    if (outFinished) flags = flags or FLAG_END_STREAM
    //根据Http2.0协议要先发送一个标识,以表示要发送数据的类型。这里表示发送一个请求头
    frameHeader(
        streamId = streamId,
        length = length.toInt(),
        type = TYPE_HEADERS,
        flags = flags
    )
    //将header的真实数据发送给服务器,sink封装了socket的输出流
    sink.write(hpackBuffer, length)

    if (byteCount > length) writeContinuationFrames(streamId, byteCount - length)
  }

    这样,请求头的发送流程就结束了,接下发送请求体

    2、发送RequestBody

override fun intercept(chain: Interceptor.Chain): Response {
    ......
    //1、发送请求头
    exchange.writeRequestHeaders(request)

    //2、请求体不为空,则发送请求体
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      //检查是否需要 服务器先返回100状态码,而后在决定是否发送请求体
      ......
      //不需要服务器返回100状态码
      if (responseBuilder == null) {
        //是否是全双工发送请求体(一边发生请求体,一边接受服务器数据)
        if (requestBody.isDuplex()) {
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          //将Http2Stream中的FramingSink进行封装为bufferedRequestBody
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          //将请求体发送给服务器
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()
        }
      } 
      ......
  }

    OKHttP会调用createRequestBody方法对数据发送器FramingSink进行封装,然后调用RequestBody的writeTo方法发送请求体,createRequestBody方法代码如下:

fun createRequestBody(request: Request, duplex: Boolean): Sink {
    this.isDuplex = duplex
    val contentLength = request.body!!.contentLength()
    eventListener.requestBodyStart(call)
    //获取FramingSink
    val rawRequestBody = codec.createRequestBody(request, contentLength)
    //将FramingSink封装到RequestBodySink
    return RequestBodySink(rawRequestBody, contentLength)
  }

override fun createRequestBody(request: Request, contentLength: Long): Sink {
    //Http2Stream中的FramingSink
    return stream!!.getSink()
  }

    以上为将FramingSink封装到RequestBodySink,接下来,会调用RequestBody的writeTo方法发送真正的请求体数据,此方法会去调用RequestBodySink的write方法,其代码如下:

private inner class RequestBodySink(
    delegate: Sink,
    /** The exact number of bytes to be written, or -1L if that is unknown. */
    private val contentLength: Long
  ) : ForwardingSink(delegate) {
    ......
    @Throws(IOException::class)
    override fun write(source: Buffer, byteCount: Long) {
      ......
      try {
        //发送请求体
        super.write(source, byteCount)
        this.bytesReceived += byteCount
      } catch (e: IOException) {
        throw complete(e)
      }
    }

     可以看出,此方法会调用ForWardingSInk的write方法,而在这个方法中,又回去调用delegate(FramingSink)的write方法,代码如下:

override fun write(source: Buffer, byteCount: Long) = delegate.write(source, byteCount)

    由此可知,其最后会调用FramingSink的write方法,此方法的代码如下:

override fun write(source: Buffer, byteCount: Long) {
      this@Http2Stream.assertThreadDoesntHoldLock()
      //先将数据写到sendBuffer中
      sendBuffer.write(source, byteCount)
      while (sendBuffer.size >= EMIT_BUFFER_SIZE) {
        //发送数据到服务器
        emitFrame(false)
      }
    }

     接下来,FramingSink会先将请求体写到sendBuffer中,然后调用emitFrame方法发送请求头,其代码如下:

private fun emitFrame(outFinishedOnLastFrame: Boolean) {
      val toWrite: Long
      val outFinished: Boolean
      synchronized(this@Http2Stream) {
        writeTimeout.enter()
        try {
          //检查是否发送结束,服务器是否关闭、已发送的数据是否超过可发送的最大数据(窗口)
          while (writeBytesTotal >= writeBytesMaximum &&
              !finished &&
              !closed &&
              errorCode == null) {
            waitForIo() // Wait until we receive a WINDOW_UPDATE for this stream.
          }
        } finally {
          writeTimeout.exitAndThrowIfTimedOut()
        }

        //计算可以发送多少数据
        toWrite = minOf(writeBytesMaximum - writeBytesTotal, sendBuffer.size)
        writeBytesTotal += toWrite
        outFinished = outFinishedOnLastFrame && toWrite == sendBuffer.size && errorCode == null
      }

      writeTimeout.enter()
      try {
        //发送数据
        connection.writeData(id, outFinished, sendBuffer, toWrite)
      } finally {
        writeTimeout.exitAndThrowIfTimedOut()
      }
    }

    此过程会先对发送数据的大小,连接是否关闭进行检查,然后,调用writeData方法进行数据的发送,代码如下:

fun writeData(
    streamId: Int,
    outFinished: Boolean,
    buffer: Buffer?,
    byteCount: Long
  ) {
    //检查是否可以发送数据(已发送数据大小是否超过窗口大小)
    var byteCount = byteCount
    while (byteCount > 0L) {
      var toWrite: Int
      synchronized(this@Http2Connection) {
        try {
          while (writeBytesTotal >= writeBytesMaximum) {
            ......
            this@Http2Connection.wait() // Wait until we receive a WINDOW_UPDATE.
          }
        } catch (e: InterruptedException) {
          Thread.currentThread().interrupt() // Retain interrupted status.
          throw InterruptedIOException()
        }
        //计算可以发送数据的大小
        toWrite = minOf(byteCount, writeBytesMaximum - writeBytesTotal).toInt()
        toWrite = minOf(toWrite, writer.maxDataLength())
        writeBytesTotal += toWrite.toLong()
      }

      byteCount -= toWrite.toLong()
      //调用Http2Writer的data方法发送数据
      writer.data(outFinished && byteCount == 0L, streamId, buffer, toWrite)
    }
  }

    此过程会再次计算可发送数据的大小,然后,调用Http2Writer的data方法发送数据,代码如下:

fun data(outFinished: Boolean, streamId: Int, source: Buffer?, byteCount: Int) {
    if (closed) throw IOException("closed")
    var flags = FLAG_NONE
    if (outFinished) flags = flags or FLAG_END_STREAM
    //以数据帧的形式发送数据
    dataFrame(streamId, flags, source, byteCount)
  }

  @Throws(IOException::class)
  fun dataFrame(streamId: Int, flags: Int, buffer: Buffer?, byteCount: Int) {
    //发送帧头
    frameHeader(
        streamId = streamId,
        length = byteCount,
        type = TYPE_DATA,
        flags = flags
    )
    //发送帧体
    if (byteCount > 0) {
      sink.write(buffer!!, byteCount.toLong())
    }
  }

    至此,请求体的发送流程就完成了。

    3、接收ResponseHeader

override fun intercept(chain: Interceptor.Chain): Response {
    ......
    //1、发送请求头
    exchange.writeRequestHeaders(request)

    //2、发送请求体
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      ......
    //3、接收响应头
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    ......
  }

     OkHttp会调用readResponseHeaders方法,以阻塞的方式读取响应头,然后,将响应头封装到ResponseBuilder中,readResponseHeaders方法代码如下:

fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
    try {
      val result = codec.readResponseHeaders(expectContinue)
      result?.initExchange(this)
      return result
    } catch (e: IOException) {
      eventListener.responseFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

    在此方法中,会调用Http2ExchangeCodec的readResponseHeaders方法接收响应头,方法代码如下:

override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
    val headers = stream!!.takeHeaders()
    val responseBuilder = readHttp2HeadersList(headers, protocol)
    return if (expectContinue && responseBuilder.code == HTTP_CONTINUE) {
      null
    } else {
      responseBuilder
    }
  }

    此方法会调用Http2Stream的takeHeaders方法接收服务端发送的响应头,此接收方法会阻塞线程,直到接收到响应头。然后,调用readHttpHeaderList方法,利用相应头创建一个ResponseBuilder(大体过程为,添加响应头,然后新建一个ResponseBuilder),在此就不对此方法展开分析。主要分析,响应头的获取过程,takeHeader方法的代码如下:

@Synchronized @Throws(IOException::class)
  fun takeHeaders(): Headers {
    readTimeout.enter()
    try {
      while (headersQueue.isEmpty() && errorCode == null) {
        waitForIo()
      }
    } 
    ......
  }

    在此方法中,循环检测headerQueue是否为null,当headerQueue不为null时,就表示接收到了响应头。(此headersQueue存在于Http2Stream中的)。可以看到此方法是一个同步方法,锁为Http2Stream对象。可以看出此方法类似“生产者消费者”模型中的消费者。生产者其实在上一篇文章的文末出现了,它是Http2Connection中的一个读取服务端数据的线程,在这里进行详细讲解。代码如下:

inner class ReaderRunnable internal constructor(
    internal val reader: Http2Reader
  ) : Http2Reader.Handler, () -> Unit {
    //这里实现了Handler接口,Handler接口的作用为处理接收到数据帧,后面会用到。
    override fun invoke() {
      var connectionErrorCode = ErrorCode.INTERNAL_ERROR
      var streamErrorCode = ErrorCode.INTERNAL_ERROR
      var errorException: IOException? = null
      try {
        reader.readConnectionPreface(this)
        //读取服务端数据
        while (reader.nextFrame(false, this)) {
        }
        ......
      } 
    }

    此线程中,存在一个while循环,它会调用Http2Reader的nextFrame方法循环读取服务端的数据,其代码如下:

fun nextFrame(requireSettings: Boolean, handler: Handler): Boolean {
    try {
      source.require(9) // Frame header size.
    } catch (e: EOFException) {
      return false // This might be a normal socket close.
    }
    //读取帧头
    val length = source.readMedium()
    if (length > INITIAL_MAX_FRAME_SIZE) {
      throw IOException("FRAME_SIZE_ERROR: $length")
    }
    val type = source.readByte() and 0xff
    val flags = source.readByte() and 0xff
    val streamId = source.readInt() and 0x7fffffff // Ignore reserved bit.
    if (logger.isLoggable(FINE)) logger.fine(frameLog(true, streamId, length, type, flags))

    if (requireSettings && type != TYPE_SETTINGS) {
      throw IOException("Expected a SETTINGS frame but was ${formattedType(type)}")
    }
    //根据帧头的类型处理帧体
    when (type) {
      TYPE_DATA -> readData(handler, length, flags, streamId)
      TYPE_HEADERS -> readHeaders(handler, length, flags, streamId)
      TYPE_PRIORITY -> readPriority(handler, length, flags, streamId)
      TYPE_RST_STREAM -> readRstStream(handler, length, flags, streamId)
      TYPE_SETTINGS -> readSettings(handler, length, flags, streamId)
      TYPE_PUSH_PROMISE -> readPushPromise(handler, length, flags, streamId)
      TYPE_PING -> readPing(handler, length, flags, streamId)
      TYPE_GOAWAY -> readGoAway(handler, length, flags, streamId)
      TYPE_WINDOW_UPDATE -> readWindowUpdate(handler, length, flags, streamId)
      else -> source.skip(length.toLong()) // Implementations MUST discard frames of unknown types.
    }

    return true
  }

    在此方法中,会读取服务端发送的数据帧,首先,会读取帧头,提取出帧头中的数据类型和streamId,然后,根据帧的类型,调用不同的方法处理帧中的真正数据。在这里,我们主要分析”“TYPE_HEADERS”响应头类型的数据,若接收到的是响应头数据,会调用readHeaders方法进行处理,代码如下:

private fun readHeaders(handler: Handler, length: Int, flags: Int, streamId: Int) {
    if (streamId == 0) throw IOException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0")

    //读取响应头
    val headerBlock = readHeaderBlock(headerBlockLength, padding, flags, streamId)
    //处理响应头
    handler.headers(endStream, streamId, -1, headerBlock)
  }

    此方法首先会调用readHeaderBlock方法从数据帧中读取响应头,具体读取过程不再继续分析,然后调用handler的headers方法去处理响应头数据。这里采用handler来处理数据,这样的方式可以降低代码的耦合度。此handler对象其实就是之前的ReaderRunnable。headers方法的代码如下:

override fun headers(
      inFinished: Boolean,
      streamId: Int,
      associatedStreamId: Int,
      headerBlock: List<Header>
    ) {
      ......
      val stream: Http2Stream?
      synchronized(this@Http2Connection) {
        stream = getStream(streamId)

        if (stream == null) {
          ......
        }
      }

      // Update an existing stream.
      stream!!.receiveHeaders(headerBlock.toHeaders(), inFinished)
    }

    在此方法中,首先,会根据streamId获取对应的Http2Stream,然后,调用Http2Stream的receiveHeaders方法接收响应头,其代码如下:

fun receiveHeaders(headers: Headers, inFinished: Boolean) {
    this@Http2Stream.assertThreadDoesntHoldLock()

    val open: Boolean
    synchronized(this) {
      if (!hasResponseHeaders || !inFinished) {
        hasResponseHeaders = true
        //添加响应头到headsQueue中
        headersQueue += headers
      } else {
        this.source.trailers = headers
      }
      if (inFinished) {
        this.source.finished = true
      }
      open = isOpen
      //唤醒等待响应头的线程
      notifyAll()
    }
    ......
  }

   此方法首先,将得到的响应头添加到headersQueue中,然后,调用notifyAll方法唤醒正在等待响应头的线程,这里,就与之前的从headersQueue中读取响应头的代码对应上了。

    4、接收ResponseBody

override fun intercept(chain: Interceptor.Chain): Response {
    ....
    //1、发送请求头
    exchange.writeRequestHeaders(request)
    ....
    //2、发送请求体
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      ......
    //3、接收响应头
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    ......

    response = if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response.newBuilder()
          .body(EMPTY_RESPONSE)
          .build()
    } else {
      //4、接收响应体
      response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    }
    ......
    return response
  }

    OkHttp会调用openResponseBody方法来接收响应体,其代码如下:

fun openResponseBody(response: Response): ResponseBody {
    try {
      val contentType = response.header("Content-Type")
      val contentLength = codec.reportedContentLength(response)
      //获取Http2Stream中的FramingSource
      val rawSource = codec.openResponseBodySource(response)
      //将FramingSource封装到RealResponseBody中
      val source = ResponseBodySource(rawSource, contentLength)
      return RealResponseBody(contentType, contentLength, source.buffer())
    } catch (e: IOException) {
      eventListener.responseFailed(call, e)
      trackFailure(e)
      throw e
    }
  }

    在此方法中,首先,获取Http2Stream中的FramingSource,FramingSource是用来读取服务器的数据帧的。然后,将FramingSource封装到RealResponseBody中,这样,我们在读取数据时,其实最终会调用FramingSource的read方法。read方法代码如下:

override fun read(sink: Buffer, byteCount: Long): Long {
      require(byteCount >= 0L) { "byteCount < 0: $byteCount" }

      while (true) {
        var tryAgain = false
        var readBytesDelivered = -1L
        var errorExceptionToDeliver: IOException? = null

        // 1. Decide what to do in a synchronized block.

        synchronized(this@Http2Stream) {
          readTimeout.enter()
          try {
            if (errorCode != null) {
              // Prepare to deliver an error.
              errorExceptionToDeliver = errorException ?: StreamResetException(errorCode!!)
            }

            if (closed) {
              throw IOException("stream closed")
            } else if (readBuffer.size > 0L) {
              // 读取接收到的数据
              readBytesDelivered = readBuffer.read(sink, minOf(byteCount, readBuffer.size))
              readBytesTotal += readBytesDelivered

              ......
            } else if (!finished && errorExceptionToDeliver == null) {
              // 目前暂无数据可以读取,进行睡眠
              waitForIo()
              tryAgain = true
            }
          } finally {
            readTimeout.exitAndThrowIfTimedOut()
          }
        }

        // 2. Do it outside of the synchronized block and timeout.
        // 因为无数据造成睡眠,现在因为有了数据被唤醒,因此继续读取数据
        if (tryAgain) {
          continue
        }
        ......
        return -1L // This source is exhausted.
      }
    }

    在介绍此段代码之前,先介绍FramingSource中的2个类变量receiveBuffer和readbuffer,readBuffer用来接收到的数据,read方法读取的就是readbuffer中的数据;receiveBuffer只能由Http2Reader方法访问,接收的也是服务器发送来的数据,而且receiveBuffer会在合适的时间,将它自己的数据转存到readBuffer中。这样设置的好处是可以加快读取效率,即用户在读取已接收的数据readbuffer时,okhttp也可保存服务器传输来的数据receiveBuffer。

    此方法为一个“消费者”,首先,检测readBuffer中是否有数据可以读取,若有则读取,若无,则进行睡眠,得到新数据的到来。在上一节,提到了ReaderRunnable,它会循环读取服务器发送的数据帧,并根据数据帧的类型进行不同的处理。此对象就可以看作是一个“生产者"。在此,接收的是响应体,因此会调用ReaderRunnable的data方法,其代码如下:

override fun data(
      inFinished: Boolean,
      streamId: Int,
      source: BufferedSource,
      length: Int
    ) {
      ......
      //处理响应体数据
      dataStream.receiveData(source, length)
      if (inFinished) {
        dataStream.receiveHeaders(EMPTY_HEADERS, true)
      }
    }

    在此方法中,会继续调用receiveData以处理响应体数据,receiveData方法代码如下:

fun receiveData(source: BufferedSource, length: Int) {
    this@Http2Stream.assertThreadDoesntHoldLock()
    //调用FramingSource的receive方法
    this.source.receive(source, length.toLong())
  }

    在此方法中,会继续调用FramingSource的receive方法,其代码如下:

internal fun receive(source: BufferedSource, byteCount: Long) {
      this@Http2Stream.assertThreadDoesntHoldLock()

      var byteCount = byteCount

      while (byteCount > 0L) {
        ......

        // 将服务器发送的数据读取到receiveBuffer
        val read = source.read(receiveBuffer, byteCount)
        if (read == -1L) throw EOFException()
        byteCount -= read

        var bytesDiscarded = 0L
        synchronized(this@Http2Stream) {
          if (closed) {
            bytesDiscarded = receiveBuffer.size
            receiveBuffer.clear()
          } else {
            //将receiveBuffer中的数据转移到readBuffer,并唤醒因receiveBuffer为空而等待的线程
            val wasEmpty = readBuffer.size == 0L
            readBuffer.writeAll(receiveBuffer)
            if (wasEmpty) {
              this@Http2Stream.notifyAll()
            }
          }
        }
        if (bytesDiscarded > 0L) {
          updateConnectionFlowControl(bytesDiscarded)
        }
      }
    }

    可以看出,此方法首先,会将服务器发送的数据读取到receiveBuffer,然后,将receivBuffer中的数据再次存放到readBuffer中,并唤醒之前的“消费者”线程。

    总体来说,读取响应体的过程如下图所示:

  响应体由FramingSource从ReaderBuffer中读取,而ReaderBuffer中的数据来自ReceiveBuffer,ReceiveBuffer中的数据由网络线程循环读取服务器发送来的数据填充。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值