上一篇博客,介绍了各种网络框架的大框区别,接下来我们依次详细的介绍各个网络框架的细节
本章最先拿最优秀的okhttp来开头。我们不以android源码中的okhttp说明,而是以大家都在使用的okhttp3来进行源码分析。(二者在设计思想上差距还是有一些的)
其实okhttp3的设计思路极其简洁,可以用分工明确来形容,暂时先忽略okio部分的网络写入和读取功能,整体流程图如下:
所有的Interceptor都有各自的功能,每个人处理之后,交给下一级处理者,最后一级处理之后,依次将结果返回给上一级,继续处理,
这里感觉是采用的责任链方式(有人可能觉得不是,责任链应该是要么处理,要么完全不处理,但我们只是处理部分,交给下一级,下一级处理之后,再继续处理,但我觉得设计模式只是一个概述,我们在design的时候,很难使用到任何一种存粹的设计模式)
整个链之间的传递只有 :Chain 和 Response两个数据,各业务之间完全没有耦合,这应该属于一个不纯的责任链模型,抽象之后如下:(所以我就称之为责任链模式,大家有其他的意见可以一起讨论)
看到这里,只要我们将整个责任链的各个环节弄明白了,就了解了整个流程。
概要介绍:
RetryAndFollowUpInterceptor :
1.重定向请求的处理,
2.重试机制的实现
BridgeInterceptor :
1.设置默认header请求(一些类型请求不设置的时候,会设置一些默认值)
2.解析返回的header
CacheInterceptor
1.整个业务的缓存逻辑,我们如果不设置缓存,默认框架是没有缓存的
ConnectInterceptor
1.建立连接池,复用连接池
2.保存输入流:source(服务器返回的数据)
3.保存输出流:sink(写入服务器的数据)
CallServerInterceptor
1.真正的网络请求,从sink中写入header和body到服务器
2.获取到header写入到response中
经过几个处理者处理之后,返回resposne,这里各个层级处理之后的header,和source,sink的实例,
此时还没有从服务器中获取过真正的body,
而是在用户端拿到response之后,通过:response.body().string()才是真正的获取body内容。
这就是整体的责任链的各个功能,下面详细介绍每个细节
1.RealCall.java
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this); //这个是同步的,所以只是将当前请求放入队列中,并没有触发任何真正的网络请求
Response result = getResponseWithInterceptorChain(); //构建责任链,并且开始执行,返回结果带有header和sink,source等网络返回
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
构建责任链相关代码:这个是可以使用者自定义的,OkHttpClient -> addInterceptor();构建interceptor时,需要符合规范
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
2. RealInterceptorChain.java
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !sameConnection(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request); //这个每次index增加,实现从interceptors里依次取出interceptor
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next); //处理之后,再次会调到RealInterceptorChain 的process,最终实现责任链的依次执行
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
3. RetryAndFollowUpInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);//这个类主要是连接池的管理,路由地址的选择,里面封装了真正网络连接部分,后面会详细介绍
int followUpCount = 0;
Response priorResponse = null;
while (true) { //通过while循环来实现重试机制
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);//这个会进入到后续的网络请求,稍后介绍
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) { //recover 是否需要去重试请求,包括,是否支持重试,是否多多个路由地址等等信息
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp = followUpRequest(response);//这个可以对一些重定向的请求做解析处理,header->location,也可以做一些代理服务器认证的处理
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {//如果是同一个host和端口和scheme,则复用之前的网络连接就可以,否则从新创建连接,或从连接池取出已存在的
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp; //request替换,从新请求
priorResponse = response;
}
}
4. BridgeInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString()); //构建header内容请求的与实体对应的MIME信息,待稍后写入
}
long contentLength = body.contentLength();
if (contentLength != -1) { //这里的原因是,如果长度未知,则使用分块编码,如果长度已知则直接设置长度
requestBuilder.header("Content-Length", Long.toString(contentLength)); //设置长度
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");//分块编码
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));//设置host,其实就是去掉scheme和://
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");//长链接
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null) {//如果用户不设置,则默认是gzip编码,接收到也会自己解压
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));//设置cookie值
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());//服务器用来区分调用者的字段,理论上没有额外的作用,但是确实有时会产生影响,详情参考我的其他博客
}
Response networkResponse = chain.proceed(requestBuilder.build());//继续接下来的责任处理者
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//保存cookie
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());//如果返回类型编码是gzip,我们也需要使用Gzip方式解析输入流,封装source
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
5.CacheInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null; //是否设置的缓存,默认是没有的
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();//这个里面有货,稍后详细介绍:6,7
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) { //缓存不可以,抛弃关闭输入流
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest); //后续的网络请求
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) { //如果有缓存内容,看看是否需要更新
if (networkResponse.code() == HTTP_NOT_MODIFIED) { //服务器返回,请求内容并没有更改,使用缓存
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);//更您缓存的内容
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (HttpHeaders.hasBody(response)) {
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);//这里是真正缓存的地方,如果可以缓存,则缓存数据
response = cacheWritingResponse(cacheRequest, response);//构建缓存数据,此时冯导response里,可以供后续使用
}
return response;
}
6.CacheStrategy.java
public Factory(long nowMillis, Request request, Response cacheResponse) {
this.nowMillis = nowMillis;
this.request = request;
this.cacheResponse = cacheResponse;
if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
Headers headers = cacheResponse.headers();
for (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
if ("Date".equalsIgnoreCase(fieldName)) { //原始服务器消息发出的时间
servedDate = HttpDate.parse(value);
servedDateString = value;
} else if ("Expires".equalsIgnoreCase(fieldName)) { //响应过期时间
expires = HttpDate.parse(value);
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) { //上次修改时间
lastModified = HttpDate.parse(value);
lastModifiedString = value;
} else if ("ETag".equalsIgnoreCase(fieldName)) { //比较两次内容是否相同,传给服务器
etag = value;
} else if ("Age".equalsIgnoreCase(fieldName)) { //从原始服务器到代理缓存形成的估算时间(以秒计,非负)这个值在后面的计算使用,
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
7.CacheStrategy -> getCandidate()
private CacheStrategy getCandidate() {
// No cached response.
if (cacheResponse == null) {
return new CacheStrategy(request, null);
}
// Drop the cached response if it's missing a required handshake.
if (request.isHttps() && cacheResponse.handshake() == null) { //如果是https并且没有进行tls的握手连接,则缓存失效
return new CacheStrategy(request, null);
}
// If this response shouldn't have been stored, it should never be used
// as a response source. This check should be redundant as long as the
// persistence store is well-behaved and the rules are constant.
if (!isCacheable(cacheResponse, request)) {
return new CacheStrategy(request, null);
}
CacheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {
return new CacheStrategy(request, null);
}
long ageMillis = cacheResponseAge(); //这个应该就是上次发送到现在的时间吧,(有可能理解有误,大家自己看一下吧)
long freshMillis = computeFreshnessLifetime(); //cache control中的 max-age 否则,从时效时间,总之就是过期时间
if (requestCaching.maxAgeSeconds() != -1) { //这个相当于取 请求缓存和 返回缓存的最小值
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
}
long minFreshMillis = 0;
if (requestCaching.minFreshSeconds() != -1) { //获取到 min-fresh 中的值:指示客户端可以接收响应时间小于当前时间加上指定时间的响应
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
}
long maxStaleMillis = 0;
CacheControl responseCaching = cacheResponse.cacheControl();
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds()); //获取max-stale 中的值:指示客户端可以接收超出超时期间的响应消息
}
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) { // 就是没有失效的缓存
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}
// Find a condition to add to the request. If the condition is satisfied, the response body
// will not be transmitted.
String conditionName;
String conditionValue;
if (etag != null) {
conditionName = "If-None-Match";
conditionValue = etag;
} else if (lastModified != null) {
conditionName = "If-Modified-Since";
conditionValue = lastModifiedString;
} else if (servedDate != null) {
conditionName = "If-Modified-Since";
conditionValue = servedDateString;
} else {
return new CacheStrategy(request, null); // No condition! Make a regular request.
}
Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue); //添加请求的header
Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();//构建请求的缓存
return new CacheStrategy(conditionalRequest, cacheResponse); //返回缓存策略
}
8. ConnectInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks); //真正建立网络连接的地方,可以选择复用连接池
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
9.StreamAllocation.java
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
int connectTimeout = client.connectTimeoutMillis();
int readTimeout = client.readTimeoutMillis();
int writeTimeout = client.writeTimeoutMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);//获取真正连接,创建||复用
HttpCodec resultCodec;
if (resultConnection.http2Connection != null) {
resultCodec = new Http2Codec(client, this, resultConnection.http2Connection);
} else {
resultConnection.socket().setSoTimeout(readTimeout);
resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
resultCodec = new Http1Codec(
client, this, resultConnection.source, resultConnection.sink);
}
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
if (!candidate.isHealthy(doExtensiveHealthChecks)) { //如果网络连接已关闭,或超时,继续查找
noNewStreams();
continue;
}
return candidate;
}
}
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) { //如果当前请求已经拥有连接,并且没有关闭,直接返回
return allocatedConnection;
}
// Attempt to get a connection from the pool.
RealConnection pooledConnection = Internal.instance.get(connectionPool, address, this); //从连接池中获取连接
if (pooledConnection != null) { //如果连接不为null,则复用返回
this.connection = pooledConnection;
return pooledConnection;
}
selectedRoute = route;
}
if (selectedRoute == null) {
selectedRoute = routeSelector.next(); //这里是便利路由ip的地方,因为每个域名会对应多个ip,他这个就是一次连接每一个ip,直到连接成功,这里有个想法,如果这里可否按照httpdns的思想,返回的ip按照访问速度快慢,这样连接的会是比较快的
synchronized (connectionPool) {
route = selectedRoute;
refusedStreamCount = 0;
}
}
RealConnection newConnection = new RealConnection(selectedRoute);//构建新的连接
synchronized (connectionPool) {
acquire(newConnection);
Internal.instance.put(connectionPool, newConnection);
this.connection = newConnection;
if (canceled) throw new IOException("Canceled");
}
newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),
connectionRetryEnabled);// 真正建立连接的地方,详细介绍
routeDatabase().connected(newConnection.route());//连接成功,从失败列表中删除当前route,
return newConnection;
}
10 .RealConnection.java
public void connect(int connectTimeout, int readTimeout, int writeTimeout, List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled) { if (protocol != null) throw new IllegalStateException("already connected"); RouteException routeException = null; ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);//这里记录了TLS协议类型,默认再DEFAULT_CONNECTION_SPECS,如果tls连接失败,可以切换尝试 if (route.address().sslSocketFactory() == null) { if (!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) { throw new RouteException(new UnknownServiceException( "CLEARTEXT communication not enabled for client")); } String host = route.address().url().host(); if (!Platform.get().isCleartextTrafficPermitted(host)) { throw new RouteException(new UnknownServiceException( "CLEARTEXT communication to " + host + " not permitted by network security policy")); } } while (protocol == null) { try { if (route.requiresTunnel()) { //如果这个https请求使用的是http代理,返回true,这个场景没搞明白,有大神,请指导一下 buildTunneledConnection(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector); } else { buildConnection(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector); } } catch (IOException e) { closeQuietly(socket); closeQuietly(rawSocket); socket = null; rawSocket = null; source = null; sink = null; handshake = null; protocol = null; if (routeException == null) { routeException = new RouteException(e); } else { routeException.addConnectException(e); } if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) { throw routeException; } } } }
/** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */ private void buildConnection(int connectTimeout, int readTimeout, int writeTimeout, ConnectionSpecSelector connectionSpecSelector) throws IOException { connectSocket(connectTimeout, readTimeout); establishProtocol(readTimeout, writeTimeout, connectionSpecSelector); }
private void connectSocket(int connectTimeout, int readTimeout) throws IOException { Proxy proxy = route.proxy(); Address address = route.address(); rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP ? address.socketFactory().createSocket() : new Socket(proxy); rawSocket.setSoTimeout(readTimeout); //设置timeout时间 try { Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);//建立socket连接 } catch (ConnectException e) { ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress()); ce.initCause(e); throw ce; } source = Okio.buffer(Okio.source(rawSocket)); //准备好输入流,以后就可以从这里读取内容到客户端 sink = Okio.buffer(Okio.sink(rawSocket)); //准备好输出流,可以往服务器写数据 }
private void establishProtocol(int readTimeout, int writeTimeout, ConnectionSpecSelector connectionSpecSelector) throws IOException { if (route.address().sslSocketFactory() != null) { connectTls(readTimeout, writeTimeout, connectionSpecSelector); //建立TLS连接 } else { protocol = Protocol.HTTP_1_1; socket = rawSocket; } if (protocol == Protocol.HTTP_2) { //http 2.0的,所以一般是进不来 socket.setSoTimeout(0); // Framed connection timeouts are set per-stream. Http2Connection http2Connection = new Http2Connection.Builder(true) .socket(socket, route.address().url().host(), source, sink) .listener(this) .build(); http2Connection.start(); // Only assign the framed connection once the preface has been sent successfully. this.allocationLimit = http2Connection.maxConcurrentStreams(); this.http2Connection = http2Connection; } else { this.allocationLimit = 1; } }
11. CallServerInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream(); //这个是在第9端的 resultCodec返回的Http1Codec
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();//stream对象
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);//这里将header写入输出流sink
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { //如果有body的话,才进行写入
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());//这里返回的会根据长度是否固定,来构造不同的sink子类
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close(); //输入流工作完成,就可以关闭了
}
httpCodec.finishRequest(); //请求结束,通过sink.flush()完成写入
Response response = httpCodec.readResponseHeaders()//解析返回的header内容
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis()) //这几个时间,在缓存相关内容都遇到过,这里存入的,
.build(); //构造了有header,handshake的返回response
int code = response.code();
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build(); //注意这里并不是获取body的地方,只是将source根据返回的header指定为某种类型,待需要的时候,才会获取
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
到这里整个网络请求就完成了,看到这里有人可能会说,这里哪里有线程池来管理整个网络请求啊,其实这里介绍的,并没有线程池来管理,相反,我们每次请求都需要自己建立线程来处理,因为主线程是不能有网络请求的,
线程池管理是针对异步处理,整体流程类似,感兴趣的自己看 RealCall ->enqueue
我这里介绍的是同步处理流程,所以并没有涉及线程池,
整篇介绍完成,可以看到okhttp有以下几个特点:
1.成功率会很高,因为他在重试中,会依次更换router ip还有更换tls协议,这是其他网络架构不具备的
2.更快,它采用复用连接池的方式,如果是相同的host和端口,则只需要服用之前的连接即可,不需要socket连接进行tcp的三次握手与四次挥手
3.缓存机制更完善,okhttp的缓存机制完整性可以与浏览器的功能一样强大,我们可以与服务器共同协商出一套节省流量的策略,完全不需要多余的代码
4.一套机制实现,文本,图片,文件的获取,只是通过source取出不同的内容,进行构建就可以。
5.更省流量,header与body读取分开,如果只需要header场景就免除了body的获取
暂时只介绍这几个,但okhttp的优势远远不止这些,他与okio的结合完成了其他网络框架很难实现的内容。
参考文档:
socket 基础知识:https://segmentfault.com/a/1190000003063859
socket 源码调用流程:http://blog.csdn.net/hello2mao/article/details/53169788
错误码返回分析原因:http://blog.csdn.net/u012422440/article/details/49914021
Http header含义:http://kb.cnblogs.com/page/92320/
uml 类图箭头含义:http://blog.csdn.net/qq429205464/article/details/48178091
transfer-Encoding :https://imququ.com/post/transfer-encoding-header-in-http.html