okhttp3 源码深入分析

上一篇博客,介绍了各种网络框架的大框区别,接下来我们依次详细的介绍各个网络框架的细节


本章最先拿最优秀的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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值