您的位置:首页 > 理论基础 > 计算机网络

OkHttp源码解析(二)——整体流程(下)

2016-07-28 00:03 579 查看

1.前言

这一篇将说一下请求体的写入以及相应的读取过程。

2.请求体写入过程

HttpEngine#readResponse

这个方法下有下面的一块代码片段。

else if (!callerWritesRequestBody) {
networkResponse = new NetworkInterceptorChain(0, networkRequest,
streamAllocation.connection()).proceed(networkRequest);
}


callerWritesRequestBody在初始化的时候为false,我们接着看NetworkInterceptorChain的proceed方法,会发现下面一段代码。

if (permitsRequestBody(request) && request.body() != null) {

Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());

BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

request.body().writeTo(bufferedRequestBody);

bufferedRequestBody.close();

}


permitsRequestBody 判断是否允许body

判断body是否为空

这里返回的requestBodyOut对象是对我们创建socket的时候的那个sike对象的包装。这里我偷个懒,具体的代码就不贴了。感兴趣的去http1xstream下追踪看看。这样,我们就把请求体也写入进去了。

3.读取响应的过程

HttpEngine#readNetworkResponse

在该方法中,有下面代码片段。

Response networkResponse = httpStream.readResponseHeaders()
.request(networkRequest)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

if (!forWebSocket) {
networkResponse = networkResponse.newBuilder()
.body(httpStream.openResponseBody(networkResponse))
.build();
}


我们重点关注httpStream.openResponseBody方法。

4.Http1xStream

Http1xStream#openResponseBody,该方法如下。

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


在getTransferStream方法中,通过层层的调用,构造出一个Source对象,并将其和response.headers组装成真正的响应体。

5.Http2XStream

Http2xStream#openResponseBody

@Override public ResponseBody openResponseBody(Response response) throws IOException {
Source source = new StreamFinishingSource(stream.getSource());
return new RealResponseBody(response.headers(), Okio.buffer(source));
}


看到,关键点在于stream.getSource,而这个stream是什么呢?这是一个FramedStrem对象,并且是在writeRequestHeaders方法中初始化的。相应代码如下

stream = framedConnection.newStream(requestHeaders, permitsRequestBody, hasResponseBody);


而framedConnection是在HttpStrem#newStream方法中初始化并传入的

resultStream = new Http2xStream(this, resultConnection.framedConnection);


而resultConnection.framedConnection则是RealConnection的framedConnection了,这个对象是在什么时候初始化的呢?是在建立连接的时候,在establishProtocol方法中。

if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) {
socket.setSoTimeout(0); // Framed connection timeouts are set per-stream.

FramedConnection framedConnection = new FramedConnection.Builder(true)
.socket(socket, route.address().url().host(), source, sink)
.protocol(protocol)
.listener(this)
.build();
framedConnection.start();

// Only assign the framed connection once the preface has been sent successfully.
this.allocationLimit = framedConnection.maxConcurrentStreams();
this.framedConnection = framedConnection;
} else {
this.allocationLimit = 1;
}


注意,上面讲socket,source,sink都传了进去。

现实new了一个对象,病start,并且把这个对象传给RealConnection的framedConnection。因此,我们看下start方法干了什么,

new Thread(readerRunnable).start();


readerRunnable = new Reader(variant.newReader(builder.source, client));


这里的Reader是一个Runnable是一个实现NamedRunnable的对象没我们需要看下他的execute方法。

@Override protected void execute() {
ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR;
ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR;
try {
if (!client) {
frameReader.readConnectionPreface();
}
while (frameReader.nextFrame(this)) {
}
connectionErrorCode = ErrorCode.NO_ERROR;
streamErrorCode = ErrorCode.CANCEL;
} catch (IOException e) {
connectionErrorCode = ErrorCode.PROTOCOL_ERROR;
streamErrorCode = ErrorCode.PROTOCOL_ERROR;
} finally {
try {
close(connectionErrorCode, streamErrorCode);
} catch (IOException ignored) {
}
Util.closeQuietly(frameReader);
}
}


可以在while里看到frameReader.nextFrame(this),而这个framewReader是Http2的Reader对象,我们看下他的方法。

variant = new Http2();


Reader(BufferedSource source, int headerTableSize, boolean client) {
this.source = source;
this.client = client;
this.continuation = new ContinuationSource(this.source);
this.hpackReader = new Hpack.Reader(headerTableSize, continuation);
}


而在在nextFrame方法里,我们就能看到读取的过程了。代码片段如下。

最后来张截图压压惊。
![这里写图片描述](http://img.blog.csdn.net/20160728000306967)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: