okhttp详解4

接下来分析Transport中HttpTransport部分,主要分析数据发送和数据接收流程。

一、重要的类简述

1.HttpTransport包装版的数据请求通道,包含httpConnection成员变量,所有的数据操作都是通过httpConnection操作。

2.HttpConnection数据操作,包含souce和sink两个成员变量,由socket包装而成。HttpConnection操作数据通过source和sink处理。

二、流程分析

1.HttpTransport.java

1
2
3
4
5
6
public void writeRequestHeaders(Request request) throws IOException {
httpEngine.writingRequestHeaders();
String requestLine = RequestLine.get(
request, httpEngine.getConnection().getRoute().getProxy().type());
httpConnection.writeRequest(request.headers(), requestLine);
}

http请求由三部分组成,分别是:请求行、消息报头、请求正文。首先创建请求行,然后通过httpConnection发送请求头和请求行。

2.HttpConnection.java

1
2
3
4
5
6
7
8
9
10
11
12
13
/** Returns bytes of a request header for sending on an HTTP transport. */
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;
}

这个部分很简单,就是按照http协议格式发送数据。

  1. HttpTransport.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override public Sink createRequestBody(Request request, long contentLength) throws IOException {
    if ("chunked".equalsIgnoreCase(request.header("Transfer-Encoding"))) {
    // Stream a request body of unknown length.
    return httpConnection.newChunkedSink();
    }

    if (contentLength != -1) {
    // Stream a request body of a known length.
    return httpConnection.newFixedLengthSink(contentLength);
    }

    throw new IllegalStateException(
    "Cannot stream a request body without chunked encoding or a known content length!");
    }

获取Request body Sink

4.HttpTransport.java

1
2
3
@Override public void finishRequest() throws IOException {
httpConnection.flush();
}

5.HttpTransport.java

1
2
3
public void flush() throws IOException {
sink.flush();
}

以上两部的目的是将之前写入sink的请求行、消息报头、请求正文发送出去。

6.HttpTransport.java

1
2
3
@Override public Response.Builder readResponseHeaders() throws IOException {
return httpConnection.readResponse();
}

7.HttpTransport.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public Response.Builder readResponse() throws IOException {
if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
throw new IllegalStateException("state: " + state);
}

try {
while (true) {
StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict());

Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message);

Headers.Builder headersBuilder = new Headers.Builder();
readHeaders(headersBuilder);
headersBuilder.add(OkHeaders.SELECTED_PROTOCOL, statusLine.protocol.toString());
responseBuilder.headers(headersBuilder.build());

if (statusLine.code != HTTP_CONTINUE) {
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
}
}
} catch (EOFException e) {
...
}
}

读取响应头。

8.HttpTransport.java

1
2
3
4
@Override public ResponseBody openResponseBody(Response response) throws IOException {
Source source = getTransferStream(response);
return new RealResponseBody(response.headers(), Okio.buffer(source));
}

获取响应正文的数据。 getTransferStream这一步很关键,获取的是HttpConnection中的一个内部类Source。

9.HttpEngine.java

1
2
3
4
public Response getResponse() {
if (userResponse == null) throw new IllegalStateException();
return userResponse;
}

之前几步已经分析,最后请求的数据会赋值给userResponse,最后获取的时候直接返回userResponse。

10.HttpEngine.java

1
2
3
4
5
6
public void releaseConnection() throws IOException {
if (transport != null && connection != null) {
transport.releaseConnectionOnIdle();
}
connection = null;
}

11.HttpTransport.java

1
2
3
4
5
6
7
@Override public void releaseConnectionOnIdle() throws IOException {
if (canReuseConnection()) {
httpConnection.poolOnIdle();
} else {
httpConnection.closeOnIdle();
}
}

释放资源,如果连接可重用则httpConnection.poolOnIdle()放置到连接池中,否则httpConnection.closeOnIdle()关闭连接。