前言
之前分析了okhttp的流程,具体的真正的http的请求没做,这里我具体分析到底是如何做到从一个字符串的地址请求到一个完整的html文档的。
正文
这里我们分析最简单的情况是http请求,而不是加密的https通讯协议。如果想读懂这个代码最好要了解整个okhttp的流程,可以读上一篇文章。
首先是RetryAndFollowUpInterceptor
这个大概是处理异常,但是有个最关键的环节,初始化了一个StreamAllocation
这个东西的功能有些强大。我们直接看文档
This class coordinates the relationship between three entities:
- Connections: physical socket connections to remote servers. These are potentially slow to establish so it is necessary to be able to cancel a connection currently being connected.
- Streams: logical HTTP request/response pairs that are layered on connections. Each connection has its own allocation limit, which defines how many concurrent streams that connection can carry. HTTP/1.x connections can carry 1 stream at a time, HTTP/2 typically carry multiple.
- Calls: a logical sequence of streams, typically an initial request and its follow up requests. We prefer to keep all streams of a single call on the same connection for better behavior and locality.
这段注释写的有点清晰啊,不过灰常笼统,他主要使Connections(连接(socket的长连接))、Stream(流,其实就是文件句柄,是在连接的上一层)、和call(请求)这三个家伙可以正常工作。说白了他就是一个分层思想,连接逻辑请求和底层socket请求的部分,具体使用时候,我们一会慢慢追究。
这个事情干完,其实RetryAndFollowUpInterceptor
在完整的请求过程中就不干什么事情了,只用等待返回即可。
接下来我们看BridgeInterceptor
他的目的只有一个,完善Request
。代码如下:
Response networkResponse = chain.proceed(requestBuilder.build());
代码还是很简单的,我就懒得一一分析,我们还是看最终的结果:
其实最关键的也就是哪个headers,作为请求的参数。
然后就到我们CacheInterceptor
这个东东就是为了加载一些cache,可是我们请求过程中没有cache,所以啥也不做,直接到网络请求。
ConnectInterceptor
这里到了我们这篇文章的核心部分,肯定也是最复杂的部分。这里初始化了StreamAllocation
中的connection
。主要代码如下:
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
public HttpCodec newStream(
......
try {
//这里才开始搞Connection,其实因为我们之前没有这种Connection,所以都是直接新建的。
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
} catch (IOException e) {
......
}
/**
* Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated
* until a healthy connection is found.
*/
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
}
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
......
result = new RealConnection(connectionPool, selectedRoute);
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
return result;
}
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
EventListener eventListener) {
connectSocket(connectTimeout, readTimeout, call, eventListener);
}
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
...
address.socketFactory().createSocket()
}
这里会有一个关键的问题貌似是个代理的问题,我们只要知道他是通过address的socketFactory,直接创建一个socket。 其实就是DefaultSocketFactory,最终调用的是
public Socket createSocket() {
return new Socket();
}
也就是废了好大劲,新建了一个socket,其他的部分需要再次慎重阅读,这里暂时方下。
我们暂时先放下来,直接读网络请求的部分
public Response intercept(Chain chain) throws IOException {
httpCodec.writeRequestHeaders(request);
}
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;
}
关键是sink是个什么,这里其实就是
Sink sink = sink((OutputStream)socket.getOutputStream(), (Timeout)timeout);
就是这个socket的文件的io操作,
这里就会发现其实http协议只是写入的部分请求的头文件,然后读取
后记
哎呀,最后部分关于route的分析实在是懒得做了,最终也没有完全讲明白http到底是写入什么内容,握手又是什么,留待以后慢慢完善吧,最近先不搞这个了。