经过前面几章的准备工作,我们终于可以和服务器进行正式的交流了。而与服务器进行正式的数据通信就发生在最后一个拦截器:ServerIntercepter.java。在解析这个类之前,需要先看一下其他的类:ExchangeCodec,这个类在上篇文章中就有涉及到,不过没有仔细讲下去。在这章中会讲一下。
ExchageCodec是一个接口,他是来规范网络请求的编码行为和网络回复的解码行为。他有2个子类 Http1ExchangeCodec 和 Http2ExchangeCodec。从名字上一看就知道,他们分别对应了Http1协议和Http2协议。下面是ExchageCodec主要的接口规范:
public interface ExchangeCodec { ... /** 将请求体转化为输出流*/ Sink createRequestBody(Request request, long contentLength) throws IOException; /** 写请求头*/ void writeRequestHeaders(Request request) throws IOException; /** 将在缓存区的请求刷新到输出流 */ void flushRequest() throws IOException; /** 通知已经完成请求动作 */ void finishRequest() throws IOException; /** 读取响应体 */ Source openResponseBodySource(Response response) throws IOException; /** 读取响应头 */ @Nullable Response.Builder readResponseHeaders(boolean expectContinue) throws IOException; ... /** 取消请求 */ void cancel(); } 复制代码
总结:总的来说,ExchageCodec.java规范了网络交互过程中的写请求和读响应的动作。具体的如下:
Http1ExchageCodec.java @Override public Sink createRequestBody(Request request, long contentLength) throws IOException { if (request.body() != null && request.body().isDuplex()) { throw new ProtocolException("Duplex connections are not supported for HTTP/1"); } //创建一个不知长度的输出流。 if ("chunked".equalsIgnoreCase(request.header("Transfer-Encoding"))) { return newChunkedSink(); } //创建一个知道长度的输出流。 if (contentLength != -1L) { return newKnownLengthSink(); } throw new IllegalStateException( "Cannot stream a request body without chunked encoding or a known content length!"); } 复制代码
总结:该方法总的来说就是根据请求的长度的确定性生成响应的流类型
@Override public void writeRequestHeaders(Request request) throws IOException { String requestLine = RequestLine.get( request, realConnection.route().proxy().type()); writeRequest(request.headers(), requestLine); } public void writeRequest(Headers headers, String requestLine) throws IOException { if (state != STATE_IDLE) throw new IllegalStateException("state: " + state); sink.writeUtf8(requestLine).writeUtf8("/r/n"); for (int i = 0, size = headers.size(); i < size; i++) { sink.writeUtf8(headers.name(i)) .writeUtf8(": ") .writeUtf8(headers.value(i)) .writeUtf8("/r/n"); } sink.writeUtf8("/r/n"); state = STATE_OPEN_REQUEST_BODY; } 复制代码
@Override public void flushRequest() throws IOException { sink.flush(); } @Override public void finishRequest() throws IOException { sink.flush(); } 复制代码
总结:他们调用的都是flush方法。所以做的都是同一件是,把缓存区的数据刷新到底层Socket。
@Override public Source openResponseBodySource(Response response) { //1. 如果没有响应体,那么构建一个读取长度为0的输入流 if (!HttpHeaders.hasBody(response)) { return newFixedLengthSource(0); } //2. 不确定长度的输入流 if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) { return newChunkedSource(response.request().url()); } //3. 确定长度的输入流 long contentLength = HttpHeaders.contentLength(response); if (contentLength != -1) { return newFixedLengthSource(contentLength); } return newUnknownLengthSource(); } 复制代码
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException { ... try { //解析响应头的String。 StatusLine statusLine = StatusLine.parse(readHeaderLine()); //构建响应体 Response.Builder responseBuilder = new Response.Builder() .protocol(statusLine.protocol) .code(statusLine.code) .message(statusLine.message) .headers(readHeaders()); if (expectContinue && statusLine.code == HTTP_CONTINUE) { return null; } else if (statusLine.code == HTTP_CONTINUE) { state = STATE_READ_RESPONSE_HEADERS; return responseBuilder; } state = STATE_OPEN_RESPONSE_BODY; return responseBuilder; } catch (EOFException e) { ... } } ///读取响应头输入流 private String readHeaderLine() throws IOException { String line = source.readUtf8LineStrict(headerLimit); headerLimit -= line.length(); return line; } 复制代码
@Override public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (RealInterceptorChain) chain; Exchange exchange = realChain.exchange(); Request request = realChain.request(); long sentRequestMillis = System.currentTimeMillis(); //1. 写入请求头 exchange.writeRequestHeaders(request); boolean responseHeadersStarted = false; Response.Builder responseBuilder = null; //2. 是否为有请求体的请求 if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { //3. 若请求头里有"100-continue",代表先只有请求头的向服务器请求。 // 需要等待服务器的响应头再进一步请求。 if ("100-continue".equalsIgnoreCase(request.header("Expect"))) { exchange.flushRequest(); responseHeadersStarted = true; exchange.responseHeadersStart(); responseBuilder = exchange.readResponseHeaders(true); } //4. 可以继续发出请求数据 if (responseBuilder == null) { //5. 将请求体写入socket if (request.body().isDuplex()) { exchange.flushRequest(); BufferedSink bufferedRequestBody = Okio.buffer( exchange.createRequestBody(request, true)); request.body().writeTo(bufferedRequestBody); } else { BufferedSink bufferedRequestBody = Okio.buffer( exchange.createRequestBody(request, false)); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } } else { exchange.noRequestBody(); if (!exchange.connection().isMultiplexed()) { exchange.noNewExchangesOnConnection(); } } } else { exchange.noRequestBody(); } //6. 通知结束请求。 if (request.body() == null || !request.body().isDuplex()) { exchange.finishRequest(); } if (!responseHeadersStarted) { exchange.responseHeadersStart(); } //7. 读取响应头 if (responseBuilder == null) { responseBuilder = exchange.readResponseHeaders(false); } //8. 构建响应体 Response response = responseBuilder .request(request) .handshake(exchange.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); //9. 如果响应码=100,需要再请求一次。 int code = response.code(); if (code == 100) { response = exchange.readResponseHeaders(false) .request(request) .handshake(exchange.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); code = response.code(); } exchange.responseHeadersEnd(response); if (forWebSocket && code == 101) { //空连接 response = response.newBuilder() .body(Util.EMPTY_RESPONSE) .build(); } else { //10.读取响应体详细 response = response.newBuilder() .body(exchange.openResponseBody(response)) .build(); } //11. 如果有close头,那么关闭连接。 if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) { exchange.noNewExchangesOnConnection(); } ... //12. 返回请求 return response; } 复制代码
总结:CallServerIntercepter的拦截逻辑很简单,总的来说就是将请求头,请求体写入Socket,然后读取Socket的响应头和响应体。而具体的IO操作,OkHttp是采用的okio,这是个优秀的IO库,具体的逻辑这里就不深挖了。具体的流程如下:
OkHttp的几个重要部分讲解就到这里全部结束了。回顾一下,我们从网络的同步/异步请求,降到它的拦截链模式。然后着重讲了几个重要的拦截器:cacheIntercepter、ConnectInterpcet和CallServerIntercepter。这几篇文章是本人在自学中,总结记录。有不对的地方欢迎指出。最后,放上一张总体架构图,有助于整体理解:
( 图片来源感谢: yq.aliyun.com/articles/78… )最后,在这里需要鸣谢以下博文:
www.jianshu.com/p/82f74db14…
www.jianshu.com/p/7624b45fb…
www.jianshu.com/p/227cee9c8…
本文引用的图片如有涉权,请联系本人删除,谢谢!