一移迫、前言
【1.1】OkHttp系列其他篇章:
- 同步請求的實現(xiàn)流程旺嬉。
- 異步請求的實現(xiàn)流程
- 重要攔截器:CacheInterceptor 的解析。
- 重要攔截器:ConnectInterceptor 的解析厨埋。
- 重要攔截器:CallServerInterceptor 的解析邪媳。
【1.2】陳述
經(jīng)過前面幾章的準(zhǔn)備工作,我們終于可以和服務(wù)器進行正式的交流了荡陷。而與服務(wù)器進行正式的數(shù)據(jù)通信就發(fā)生在最后一個攔截器:ServerIntercepter.java雨效。在解析這個類之前,需要先看一下其他的類:ExchangeCodec废赞,這個類在上篇文章中就有涉及到徽龟,不過沒有仔細講下去。在這章中會講一下唉地。
二据悔、ExchangeCodec
【2.1】簡介
ExchageCodec是一個接口传透,他是來規(guī)范網(wǎng)絡(luò)請求的編碼行為和網(wǎng)絡(luò)回復(fù)的解碼行為。他有2個子類 Http1ExchangeCodec 和 Http2ExchangeCodec极颓。從名字上一看就知道朱盐,他們分別對應(yīng)了Http1協(xié)議和Http2協(xié)議。下面是ExchageCodec主要的接口規(guī)范:
public interface ExchangeCodec {
...
/** 將請求體轉(zhuǎn)化為輸出流*/
Sink createRequestBody(Request request, long contentLength) throws IOException;
/** 寫請求頭*/
void writeRequestHeaders(Request request) throws IOException;
/** 將在緩存區(qū)的請求刷新到輸出流 */
void flushRequest() throws IOException;
/** 通知已經(jīng)完成請求動作 */
void finishRequest() throws IOException;
/** 讀取響應(yīng)體 */
Source openResponseBodySource(Response response) throws IOException;
/** 讀取響應(yīng)頭 */
@Nullable Response.Builder readResponseHeaders(boolean expectContinue) throws IOException;
...
/** 取消請求 */
void cancel();
}
總結(jié): 總的來說菠隆,ExchageCodec.java規(guī)范了網(wǎng)絡(luò)交互過程中的寫請求和讀響應(yīng)的動作兵琳。具體的如下:
- 將請求體轉(zhuǎn)化為輸出流。
- 寫請求頭浸赫。
- 將在請求刷新到底層的Socket。
- 通知完成請求動作赃绊。
- 讀響應(yīng)頭既峡。
- 讀響應(yīng)體。
- 取消請求碧查。
三运敢、Http1ExchangeCodec
【3.1】createRequestBody()
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");
}
//創(chuàng)建一個不知長度的輸出流。
if ("chunked".equalsIgnoreCase(request.header("Transfer-Encoding"))) {
return newChunkedSink();
}
//創(chuàng)建一個知道長度的輸出流忠售。
if (contentLength != -1L) {
return newKnownLengthSink();
}
throw new IllegalStateException(
"Cannot stream a request body without chunked encoding or a known content length!");
}
總結(jié): 該方法總的來說就是根據(jù)請求的長度的確定性生成響應(yīng)的流類型
【3.2】 writeRequestHeaders():寫入請求頭
@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;
}
【3.3】flushRequest()/finishRequest()
@Override public void flushRequest() throws IOException {
sink.flush();
}
@Override public void finishRequest() throws IOException {
sink.flush();
}
總結(jié): 他們調(diào)用的都是flush方法传惠。所以做的都是同一件是,把緩存區(qū)的數(shù)據(jù)刷新到底層Socket稻扬。
【3.4】openResponseBodySource():讀取響應(yīng)體
@Override public Source openResponseBodySource(Response response) {
//1. 如果沒有響應(yīng)體卦方,那么構(gòu)建一個讀取長度為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();
}
【3.5】readResponseHeaders():讀取響應(yīng)頭
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
...
try {
//解析響應(yīng)頭的String。
StatusLine statusLine = StatusLine.parse(readHeaderLine());
//構(gòu)建響應(yīng)體
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) {
...
}
}
///讀取響應(yīng)頭輸入流
private String readHeaderLine() throws IOException {
String line = source.readUtf8LineStrict(headerLimit);
headerLimit -= line.length();
return line;
}
三泰佳、CallServerIntercepter.java: 最后一個攔截器
@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"悔橄,代表先只有請求頭的向服務(wù)器請求蔚万。
// 需要等待服務(wù)器的響應(yīng)頭再進一步請求。
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
responseBuilder = exchange.readResponseHeaders(true);
}
//4. 可以繼續(xù)發(fā)出請求數(shù)據(jù)
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. 通知結(jié)束請求。
if (request.body() == null || !request.body().isDuplex()) {
exchange.finishRequest();
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
//7. 讀取響應(yīng)頭
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
//8. 構(gòu)建響應(yīng)體
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
//9. 如果響應(yīng)碼=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.讀取響應(yīng)體詳細
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
//11. 如果有close頭榔组,那么關(guān)閉連接雳殊。
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
...
//12. 返回請求
return response;
}
總結(jié): CallServerIntercepter的攔截邏輯很簡單,總的來說就是將請求頭臀晃,請求體寫入Socket觉渴,然后讀取Socket的響應(yīng)頭和響應(yīng)體。而具體的IO操作徽惋,OkHttp是采用的okio疆拘,這是個優(yōu)秀的IO庫,具體的邏輯這里就不深挖了寂曹。具體的流程如下:
- 寫入請求頭哎迄。
- 如果請求頭里有"100-continue", 代表先將請求頭發(fā)送給服務(wù)器回右,看服務(wù)器的響應(yīng)決定是否進行下一步請求體的發(fā)送。
- 寫入請求體漱挚,并發(fā)送請求翔烁。
- 讀取響應(yīng)體,并構(gòu)建一個Resonse
- 如果響應(yīng)碼為100旨涝,需要再請求一次蹬屹。
- 讀取詳細的響應(yīng)體。
- 如果響應(yīng)頭有“close”白华,那么關(guān)閉這條連接慨默。
- 返回響應(yīng)。
OkHttp的幾個重要部分講解就到這里全部結(jié)束了弧腥∠萌。回顧一下,我們從網(wǎng)絡(luò)的同步/異步請求管搪,降到它的攔截鏈模式虾攻。然后著重講了幾個重要的攔截器:cacheIntercepter、ConnectInterpcet和CallServerIntercepter更鲁。這幾篇文章是本人在自學(xué)中霎箍,總結(jié)記錄。有不對的地方歡迎指出澡为。最后漂坏,放上一張總體架構(gòu)圖,有助于整體理解:
(圖片來源感謝:https://yq.aliyun.com/articles/78105?spm=5176.100239.blogcont78104.10.FlPFWr)
最后媒至,在這里需要鳴謝以下博文:
http://www.reibang.com/p/82f74db14a18
http://www.reibang.com/p/7624b45fbdc1
http://www.reibang.com/p/227cee9c8d15
本文引用的圖片如有涉權(quán)樊拓,請聯(lián)系本人刪除,謝謝塘慕!