主流開源庫(kù)-OkHttp源碼分析

如對(duì)網(wǎng)絡(luò)知識(shí)不熟悉吨些,可跳到最后補(bǔ)一下網(wǎng)絡(luò)基礎(chǔ)
文字總結(jié):
先創(chuàng)建我們的request里面包括我們的Host請(qǐng)求參數(shù)等统阿,okhttpClient的.newCall(request)方法生成一個(gè)RealCall,RealCall方法是我們的重點(diǎn)掌敬,首先執(zhí)行enqueue方法拦英,RealCall類會(huì)調(diào)用一個(gè)Dispatcher的線程池類入客,把我們一個(gè)異步Call傳給Dispatcher線程池邦尊,我們Dispatcher類首先判斷一下真正執(zhí)行的任務(wù)的數(shù)量<64瘩缆,真在執(zhí)行host的是否<5关拒,然后把我們的異步Call調(diào)用線程池中執(zhí)行,而異步Call的run()方法最終還是調(diào)用RealCall的execute方法庸娱,execute方法還是調(diào)用的一個(gè)得到響應(yīng)的getResponseWithInterceptorChain()着绊,調(diào)用我們的攔截器。


okhttp最基本的請(qǐng)求

添加依賴:
implementation 'com.squareup.okhttp3:okhttp:3.14.5'
//請(qǐng)求客戶端
      OkHttpClient okHttpClient = new OkHttpClient();
      //使用builder模式生成request對(duì)象
      Request request = new Request.Builder()
              .url("https://i0.hdslb.com/bfs/article/2b2d2f26caa0ebf07dbe617be4a5ba919eaa0724.jpg@1320w_742h.webp")
              .build();
      //請(qǐng)求同步
      Response response = okHttpClient.newCall(request).execute();

      //請(qǐng)求異步熟尉,開啟一個(gè)子線程归露,不會(huì)阻塞
      okHttpClient.newCall(request).enqueue(new Callback() {
          @Override
          public void onFailure(Call call, IOException e) {

          }

          @Override
          public void onResponse(Call call, Response response) throws IOException {

          }
      });

先看看OkHttp最要的類之一RealCall

#RealCall類

//異步的調(diào)用方法,參數(shù)callBack是個(gè)接口實(shí)例斤儿,最終還是會(huì)調(diào)用RealCall的execute()方法
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));//把一個(gè)異步Call給線程池請(qǐng)求執(zhí)行剧包,最終還是調(diào)用execute方法
}

#線程池類dispatcher的enqueue
synchronized void enqueue(AsyncCall call) {
 //判斷正在執(zhí)行的數(shù)量<64,正在執(zhí)行請(qǐng)求Host<5
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call); //線程池執(zhí)行請(qǐng)求恐锦,實(shí)際回到RealCall.execure()方法
  } else {
    readyAsyncCalls.add(call);
  }
}

//內(nèi)部異步Call
final class AsyncCall extends NamedRunnable {
  @Override protected void execute() {
    boolean signalledCallback = false;
    transmitter.timeoutEnter();
    try {
      Response response = getResponseWithInterceptorChain();
      signalledCallback = true;
      responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
      if (signalledCallback) {
        // Do not signal the callback twice!
        Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
      } else {
        responseCallback.onFailure(RealCall.this, e);
      }
    } finally {
      client.dispatcher().finished(this);
    }
  }
}
public abstract class NamedRunnable implements Runnable {
......
@Override 
public final void run() {
try {
  execute();//在線程池執(zhí)行的時(shí)候,又回到RealCall.execute方法
} 
protected abstract void execute();
}


//主要作用是調(diào)用得到響應(yīng)的getResponseWithInterceptorChain()方法
@Override public Response execute() throws IOException {
synchronized (this) {
  if (executed) throw new IllegalStateException("Already Executed");
  executed = true;
}
try {
  client.dispatcher().executed(this); //加到同步請(qǐng)求的隊(duì)列
  return getResponseWithInterceptorChain();//調(diào)用攔截器
} finally {
  client.dispatcher().finished(this); //通知dispatcher任務(wù)執(zhí)行完了疆液。
}
}

1.創(chuàng)建客戶端okhttpClient源碼:

OkHttpClient okHttpClient = new OkHttpClient();

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
public OkHttpClient() {
     this(new Builder());
}
//Builder.builder()方法調(diào)用這個(gè)構(gòu)造函數(shù)并把builder傳進(jìn)來(lái)
OkHttpClient(Builder builder) {
  this.dispatcher = builder.dispatcher;
  this.proxy = builder.proxy;
  ...
}

public static final class Builder {
public Builder() {
    dispatcher = new Dispatcher();
    protocols = DEFAULT_PROTOCOLS;
    connectionSpecs = DEFAULT_CONNECTION_SPECS;
    proxySelector = ProxySelector.getDefault();
    cookieJar = CookieJar.NO_COOKIES;
    socketFactory = SocketFactory.getDefault();
    hostnameVerifier = OkHostnameVerifier.INSTANCE;
    certificatePinner = CertificatePinner.DEFAULT;
    proxyAuthenticator = Authenticator.NONE;
    authenticator = Authenticator.NONE;
    connectionPool = new ConnectionPool();
    dns = Dns.SYSTEM;
    followSslRedirects = true;
    followRedirects = true;
    retryOnConnectionFailure = true;
    connectTimeout = 10_000;
    readTimeout = 10_000;
    writeTimeout = 10_000;
  }
public OkHttpClient build() {
    return new OkHttpClient(this);
  }
}
}

2.建立Request(http報(bào)文)

Request:包括:請(qǐng)求地址url,請(qǐng)求方法method一铅,請(qǐng)求頭head,請(qǐng)求數(shù)據(jù)requestBody,標(biāo)志位tag

Request request = new Request.Builder().url("url").build();

public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;

private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
//build()最終調(diào)用這個(gè)方法
Request(Builder builder) {
  this.url = builder.url;
  this.method = builder.method;
  this.headers = builder.headers.build();
  this.body = builder.body;
  this.tags = Util.immutableMap(builder.tags);
}
//寫好默認(rèn)的方法和頭堕油,url給我們寫
public static class Builder {
  @Nullable HttpUrl url;
  String method;
  Headers.Builder headers;
  @Nullable RequestBody body;

  Map<Class<?>, Object> tags = Collections.emptyMap();

  public Builder() {
   //默認(rèn)使用get方法
    this.method = "GET";
    this.headers = new Headers.Builder();
  }

  Builder(Request request) {
    this.url = request.url;
    this.method = request.method;
    this.body = request.body;
    this.tags = request.tags.isEmpty()
        ? Collections.emptyMap()
        : new LinkedHashMap<>(request.tags);
    this.headers = request.headers.newBuilder();
  }

//請(qǐng)求頭部
public Builder header(String name, String value) {
    headers.set(name, value);
    return this;
  }

public Builder addHeader(String name, String value) {
    headers.add(name, value);
    return this;
  }
public Builder method(String method, RequestBody body) {
    if (method == null) throw new NullPointerException("method == null");
    if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
    if (body != null && !HttpMethod.permitsRequestBody(method)) {
      throw new IllegalArgumentException("method " + method + " must not have a request body.");
    }
    if (body == null && HttpMethod.requiresRequestBody(method)) {
      throw new IllegalArgumentException("method " + method + " must have a request body.");
    }
    this.method = method;
    this.body = body;
    return this;
  }

  public Request build() {
    if (url == null) throw new IllegalStateException("url == null");
    return new Request(this);
  }
}

Dispatcher線程池介紹

public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;

/** Executes calls. Created lazily. */
private ExecutorService executorService;

/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

public Dispatcher() {
}

public synchronized ExecutorService executorService() {
  if (executorService == null) {
    executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
  }
  return executorService;
}

synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else {
    readyAsyncCalls.add(call);
  }
}

synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}

synchronized void finished(Call call) {
  if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}

核心重點(diǎn)攔截器方法得到響應(yīng)

getResponseWithInterceptorChain():
文字總結(jié)攔截器的作用:攔截器可以通過(guò)Chain參數(shù)拿到上一個(gè)的攔截器的request潘飘,并通過(guò)執(zhí)行Chain接口的proceed(request)把request傳給下一個(gè)攔截器并從一個(gè)攔截器中拿到response。這是一種責(zé)任鏈的設(shè)計(jì)模式掉缺。

//
public interface Interceptor {
Response intercept(Chain chain) throws IOException;

interface Chain {
  Request request();

  Response proceed(Request request) throws IOException;
  ...
}
}
//實(shí)現(xiàn)Interceptor.Chain接口的實(shí)例
public final class RealInterceptorChain implements Interceptor.Chain {

@Override public Request request() {
  return request;
}

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, transmitter, exchange);
}

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
    throws IOException {
        RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
      index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
       Interceptor interceptor = interceptors.get(index);
       Response response = interceptor.intercept(next);
       ...
      return response 
}
}

重試攔截器:RetryAndFollowUpInterceptor

文字總結(jié):處理重試的攔截器福也,會(huì)處理一些異常,如果這個(gè)異常不是致命的則從返回一個(gè)request給下級(jí)攀圈,如果是致命的則將錯(cuò)誤給上級(jí)。
再重新生成request時(shí)會(huì)對(duì)狀態(tài)碼進(jìn)行檢查峦甩,比如重定向的307赘来,就會(huì)從返回的響應(yīng)中獲取新的路徑,生成一個(gè)新的request給下級(jí)重新發(fā)起一次請(qǐng)求凯傲,如果得到的request為null則返回現(xiàn)在的response給上級(jí)犬辰。

public final class RetryAndFollowUpInterceptor implements Interceptor {
public RetryAndFollowUpInterceptor(OkHttpClient client) {
  this.client = client;
}

@Override public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
   //一個(gè)多路復(fù)用
  Transmitter transmitter = realChain.transmitter();

   while (true) {
    transmitter.prepareToConnect(request);

    if (transmitter.isCanceled()) {
      throw new IOException("Canceled");
    }

    Response response;
    boolean success = false;
    try {
      //在try中執(zhí)行拿到的response
      response = realChain.proceed(request, transmitter, null);
      success = true;
    } catch (RouteException e) {
      //判斷是否是致命的異常,如果是則拋出錯(cuò)誤給上一級(jí)冰单,如果不是則continue下一次請(qǐng)求幌缝。
      if (!recover(e.getLastConnectException(), transmitter, false, request)) {
        throw e.getFirstConnectException();
      }
      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, transmitter, requestSendStarted, request)) throw e;
      continue;
    } finally {
      // The network call threw an exception. Release any resources.
      if (!success) {
        transmitter.exchangeDoneDueToException();
      }
    }

    // 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();
    }

    Exchange exchange = Internal.instance.exchange(response);
    Route route = exchange != null ? exchange.connection().route() : null;
   //根據(jù)下級(jí)返回的response,判斷狀態(tài)碼诫欠,并重新確定request
    Request followUp = followUpRequest(response, route);

   //如果新的request是null則返回response
    if (followUp == null) {
      if (exchange != null && exchange.isDuplex()) {
        transmitter.timeoutEarlyExit();
      }
      return response;
    }

    RequestBody followUpBody = followUp.body();
    if (followUpBody != null && followUpBody.isOneShot()) {
      return response;
    }

    closeQuietly(response.body());
    if (transmitter.hasExchange()) {
      exchange.detachWithViolence();
    }

    if (++followUpCount > MAX_FOLLOW_UPS) {
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    request = followUp;
    priorResponse = response;
  }
}

}

橋接攔截器:BridgeInterceptor

文字總結(jié):給請(qǐng)求頭部做一些通用的設(shè)置涵卵,判斷返回的response是否用到壓縮并把它變成GzipSource,和保存cookie荒叼。

public final class BridgeInterceptor implements Interceptor {
private final CookieJar cookieJar;

public BridgeInterceptor(CookieJar cookieJar) {
  this.cookieJar = cookieJar;
}

@Override public Response intercept(Chain chain) throws IOException {
 //得到上一級(jí)的request
  Request userRequest = chain.request();
  Request.Builder requestBuilder = userRequest.newBuilder();
  //拿到RequestBody 
  RequestBody body = userRequest.body();
 //根據(jù)RequestBody設(shè)置一些請(qǐng)求首部的參數(shù)
  if (body != null) {
    MediaType contentType = body.contentType();
    if (contentType != null) {
      requestBuilder.header("Content-Type", contentType.toString());
    }

    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");
    }
  }
  //判斷請(qǐng)求首部參數(shù)有無(wú)并添加
  if (userRequest.header("Host") == null) {
    requestBuilder.header("Host", hostHeader(userRequest.url(), false));
  }

  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 && userRequest.header("Range") == null) {
    transparentGzip = true;
    requestBuilder.header("Accept-Encoding", "gzip");
  }

  List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
  if (!cookies.isEmpty()) {
    requestBuilder.header("Cookie", cookieHeader(cookies));
  }

  if (userRequest.header("User-Agent") == null) {
    requestBuilder.header("User-Agent", Version.userAgent());
  }

  //從下級(jí)拿到Response
  Response networkResponse = chain.proceed(requestBuilder.build());

  HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

  Response.Builder responseBuilder = networkResponse.newBuilder()
      .request(userRequest);
   //判斷是否是gzip壓縮轿偎,生成GzipSource
  if (transparentGzip
      && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
      && HttpHeaders.hasBody(networkResponse)) {
    GzipSource responseBody = new GzipSource(networkResponse.body().source());
    Headers strippedHeaders = networkResponse.headers().newBuilder()
        .removeAll("Content-Encoding")
        .removeAll("Content-Length")
        .build();
    responseBuilder.headers(strippedHeaders);
    String contentType = networkResponse.header("Content-Type");
    responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
  }

  return responseBuilder.build();
}

/** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
private String cookieHeader(List<Cookie> cookies) {
  StringBuilder cookieHeader = new StringBuilder();
  for (int i = 0, size = cookies.size(); i < size; i++) {
    if (i > 0) {
      cookieHeader.append("; ");
    }
    Cookie cookie = cookies.get(i);
    cookieHeader.append(cookie.name()).append('=').append(cookie.value());
  }
  return cookieHeader.toString();
}
}

緩存攔截器:CacheInterceptor

文字總結(jié):緩存攔截器會(huì)根據(jù)請(qǐng)求的request和拿到的緩存生成一個(gè)緩存策略,緩存策略有需要請(qǐng)求的request和緩存response被廓,根據(jù)緩存策略的request和response是否為空進(jìn)行下一步的操作坏晦,比如兩個(gè)都為null就返回504,request為Null則直接返回本地緩存嫁乘,根據(jù)下級(jí)返回的response查看是否狀態(tài)碼是否為304昆婿。
在緩存可用的情況下,讀取本地的緩存的數(shù)據(jù)蜓斧,如果沒(méi)有直接去服務(wù)器仓蛆,如果有首先判斷有沒(méi)有緩存策略,然后判斷有沒(méi)有過(guò)期挎春,如果沒(méi)有過(guò)期直接拿緩存多律,如果過(guò)期了需要添加一些之前頭部信息如:If-Modified-Since 痴突,這個(gè)時(shí)候后臺(tái)有可能會(huì)給你返回 304 代表你還是可以拿本地緩存,每次讀取到新的響應(yīng)后做一次緩存狼荞。

public final class CacheInterceptor implements Interceptor {
public CacheInterceptor(@Nullable InternalCache cache) {
  this.cache = cache;
}

@Override public Response intercept(Chain chain) throws IOException {
 //根據(jù)請(qǐng)求辽装,判斷是否有緩存
  Response cacheCandidate = cache != null
      ? cache.get(chain.request())
      : null;

  long now = System.currentTimeMillis();
  //根據(jù)請(qǐng)求和得到的緩存拿到一個(gè)緩存策略
  CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
  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.
  }

  // 如果請(qǐng)求的 networkRequest 是空,緩存的 cacheResponse 是空我就返回 504相味。 指定該數(shù)據(jù)只從緩存獲取
  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) {
  //不用請(qǐng)求拾积,直接返回緩存response
    return cacheResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .build();
  }

  //否則,把networkRequest重新請(qǐng)求一遍
  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());
    }
  }

  // 如果本地的緩存不為空丰涉,再次請(qǐng)求得到的response的狀態(tài)碼為304拓巧,則復(fù)用之前的response給上級(jí)
  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();

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 (cache != null) {
    if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
      // Offer this request to the cache.
      CacheRequest cacheRequest = cache.put(response);
      return cacheWritingResponse(cacheRequest, response);
    }

    if (HttpMethod.invalidatesCache(networkRequest.method())) {
      try {
        cache.remove(networkRequest);
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
    }
  }

  return response;
}
}

連接攔截器:ConnectInterceptor

文字總結(jié):

findHealthyConnection() 找一個(gè)連接,首先判斷有沒(méi)有健康的一死,沒(méi)有就創(chuàng)建(建立Scoket,握手連接)肛度,連接緩存。
封裝 HttpCodec 里面封裝了 okio 的 Source(輸入) 和 Sink (輸出)投慈,我們通過(guò) HttpCodec 就可以操作 Socket的輸入輸出承耿,我們就可以像服務(wù)器寫數(shù)據(jù)和讀取返回?cái)?shù)據(jù)

public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;

public ConnectInterceptor(OkHttpClient client) {
  this.client = client;
}

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Request request = realChain.request();
  Transmitter transmitter = realChain.transmitter();

  // We need the network to satisfy this request. Possibly for validating a conditional GET.
  boolean doExtensiveHealthChecks = !request.method().equals("GET");
 //建立一個(gè)多路復(fù)用
  Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);

  return realChain.proceed(request, transmitter, exchange);
}
}

CallServerInterceptor

文字總結(jié):寫數(shù)據(jù)和讀取數(shù)據(jù)
寫頭部信息,寫body表單信息等等

public final class CallServerInterceptor implements Interceptor {
private final boolean forWebSocket;

public CallServerInterceptor(boolean forWebSocket) {
  this.forWebSocket = forWebSocket;
}

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Exchange exchange = realChain.exchange();
  Request request = realChain.request();

  long sentRequestMillis = System.currentTimeMillis();

  exchange.writeRequestHeaders(request);

  boolean responseHeadersStarted = false;
  Response.Builder responseBuilder = null;
  if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
    // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
    // Continue" response before transmitting the request body. If we don't get that, return
    // what we did get (such as a 4xx response) without ever transmitting the request body.
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
      exchange.flushRequest();
      responseHeadersStarted = true;
      exchange.responseHeadersStart();
      responseBuilder = exchange.readResponseHeaders(true);
    }

    if (responseBuilder == null) {
      if (request.body().isDuplex()) {
        // Prepare a duplex body so that the application can send a request body later.
        exchange.flushRequest();
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, true));
        request.body().writeTo(bufferedRequestBody);
      } else {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, false));
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      }
    } else {
      exchange.noRequestBody();
      if (!exchange.connection().isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
        // from being reused. Otherwise we're still obligated to transmit the request body to
        // leave the connection in a consistent state.
        exchange.noNewExchangesOnConnection();
      }
    }
  } else {
    exchange.noRequestBody();
  }

  if (request.body() == null || !request.body().isDuplex()) {
    exchange.finishRequest();
  }

  if (!responseHeadersStarted) {
    exchange.responseHeadersStart();
  }

  if (responseBuilder == null) {
    responseBuilder = exchange.readResponseHeaders(false);
  }

  Response response = responseBuilder
      .request(request)
      .handshake(exchange.connection().handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();

  int code = response.code();
  if (code == 100) {
    // server sent a 100-continue even though we did not request one.
    // try again to read the actual response
    response = exchange.readResponseHeaders(false)
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    code = response.code();
  }

  exchange.responseHeadersEnd(response);

  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(exchange.openResponseBody(response))
        .build();
  }

  if ("close".equalsIgnoreCase(response.request().header("Connection"))
      || "close".equalsIgnoreCase(response.header("Connection"))) {
    exchange.noNewExchangesOnConnection();
  }

  if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
    throw new ProtocolException(
        "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
  }

  return response;
}
}

攔截器總結(jié):首先調(diào)用retry攔截它可以在傳給下級(jí)request得到response的過(guò)程中對(duì)一些非致命的異常進(jìn)行再一次的請(qǐng)求伪煤,對(duì)于下級(jí)返回的response對(duì)狀態(tài)碼進(jìn)行判斷做再次的請(qǐng)求或者給上一級(jí)加袋,對(duì)于一些致命異常也是返回給上一級(jí)。第二個(gè)是BridgeInterceptor攔截器抱既,它會(huì)對(duì)請(qǐng)求的首部做一些通用的設(shè)置职烧,并對(duì)返回的response進(jìn)行是否需要壓縮的判斷返回gzip和保存response的cookie。第三個(gè)是cache緩存攔截防泵,會(huì)根據(jù)請(qǐng)求拿到本地的緩存蚀之,并根據(jù)請(qǐng)求和本地緩存拿到生成的緩存策略,根據(jù)緩存策略的request和response判斷是直接返回緩存捷泞,還是繼續(xù)請(qǐng)求恬总,并根據(jù)返回的response判斷狀態(tài)碼是否繼續(xù)返回本地緩存。第四connect連接攔截器主要是與服務(wù)器建立TCL的握手連接肚邢,這里的連接會(huì)使用多路復(fù)用壹堰,就是沒(méi)有斷開連接之前可以有多個(gè)請(qǐng)求共享一個(gè)連接,端對(duì)端是使用socket連接的骡湖。第五:CallServer攔截器主要是讀寫數(shù)據(jù)贱纠。

getResponseWithInterceptorChain得到最上級(jí)的resonse響應(yīng)。

Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); //增加自己定義的攔截器
interceptors.add(new RetryAndFollowUpInterceptor(client));//重試攔截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));//橋接攔截器响蕴,為請(qǐng)求頭部添加數(shù)據(jù)
interceptors.add(new CacheInterceptor(client.internalCache()));//緩存攔截器谆焊,
interceptors.add(new ConnectInterceptor(client));//連接攔截器妹孙,負(fù)責(zé)請(qǐng)求和服務(wù)器連接
if (!forWebSocket) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));//網(wǎng)絡(luò)服務(wù)端的連接

//最終把攔截器責(zé)任鏈表
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
    originalRequest, this, client.connectTimeoutMillis(),
    client.readTimeoutMillis(), client.writeTimeoutMillis());

boolean calledNoMoreExchanges = false;
try {
 //reTry攔截器返回的response
  Response response = chain.proceed(originalRequest);
  if (transmitter.isCanceled()) {
    closeQuietly(response);
    throw new IOException("Canceled");
  }
  return response;
} catch (IOException e) {
  calledNoMoreExchanges = true;
  throw transmitter.noMoreExchanges(e);
} finally {
  if (!calledNoMoreExchanges) {
    transmitter.noMoreExchanges(null);
  }
}
}

RealConnect類

ublic final class RealConnection extends Http2Connection.Listener implements Connection {
private static final String NPE_THROW_WITH_NULL = "throw with null exception";
private static final int MAX_TUNNEL_ATTEMPTS = 21;

public final RealConnectionPool connectionPool;
private final Route route;

// The fields below are initialized by connect() and never reassigned.
//以下字段由connect()初始化室埋,并且從不重新分配金刁。

/** 低級(jí)TCP套接字. */
private Socket rawSocket;

private Socket socket;
//通過(guò)這個(gè)對(duì)象握手
private Handshake handshake;
private Protocol protocol; //協(xié)議
private Http2Connection http2Connection; //http2.0
private BufferedSource source; //輸入流
private BufferedSink sink; //輸出流

總結(jié):OkHttp的底層是通過(guò)Java的Socket發(fā)送HTTP請(qǐng)求與接受響應(yīng)的(這也好理解惫企,HTTP就是基于TCP協(xié)議的),但是OkHttp實(shí)現(xiàn)了連接池的概念罐孝,即對(duì)于同一主機(jī)的多個(gè)請(qǐng)求呐馆,其實(shí)可以公用一個(gè)Socket連接,而不是每次發(fā)送完HTTP請(qǐng)求就關(guān)閉底層的Socket莲兢,這樣就實(shí)現(xiàn)了連接池的概念汹来。而OkHttp對(duì)Socket的讀寫操作使用的OkIo庫(kù)進(jìn)行了一層封裝。

image.png

http的相關(guān)知識(shí)

HTTP,FTP,DNS,TCP,UDP,IP
OSI七層協(xié)議:應(yīng)用層改艇、表示層收班、會(huì)話層、傳輸層谒兄、網(wǎng)絡(luò)層摔桦、數(shù)據(jù)鏈路層、物理層
五層:應(yīng)用層承疲、傳輸層邻耕、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層纪隙、物理層

image.png

TCP的三次握手和四次分手

文字總結(jié):
三次握手之后才開始發(fā)送數(shù)據(jù)
客戶端先發(fā)一條信息給服務(wù)端說(shuō)能聽到嗎
服務(wù)端就發(fā)送一條信息并帶ACK給客戶端說(shuō)我能聽到,你能聽到嗎
客戶端就發(fā)送一條信息給服務(wù)端說(shuō)我能聽到

四次揮手:
掛了
好的扛或。掛了
好的


image.png

Http報(bào)文

請(qǐng)求報(bào)文(Qequest):請(qǐng)求頭(首部)+空行+請(qǐng)求數(shù)據(jù)
響應(yīng)報(bào)文(Response):響應(yīng)頭(首部)+空行+響應(yīng)數(shù)據(jù)

Http首部

image.png

image.png

請(qǐng)求首部
Accept:用戶代理可處理的媒體類型
Accept-Charset:優(yōu)先的字符集
Accept-Language:優(yōu)先的語(yǔ)言(自然語(yǔ)言)
Accept-Encoding:優(yōu)先的內(nèi)容編碼
If-Modified-Since:比較資源的更新時(shí)間
If-Range:資源未更新時(shí)發(fā)送實(shí)體 Byte 的范圍請(qǐng)求
Cookie: 設(shè)置Cookie

響應(yīng)首部
Cache-Control:控制緩存的行為
set-Cookie: 設(shè)置Cookie
Location:令客戶端重定向至指定 URI
Expires:實(shí)體主體過(guò)期的日期時(shí)間
Last-Modified:資源的最后修改日期時(shí)間
Status Code: 響應(yīng)的狀態(tài)碼

cookie:服務(wù)端發(fā)過(guò)來(lái)的信息绵咱,在客戶端保存并再請(qǐng)求的時(shí)候發(fā)給服務(wù)端,cookie有個(gè)過(guò)期時(shí)間
session:在服務(wù)端保存客戶端的信息熙兔,斷開鏈接時(shí)則失效
token:服務(wù)端給客戶端的一個(gè)id身份號(hào)碼悲伶。

Http緩存

cache-Control(緩存策略):Public、private住涉、no_cache麸锉、max_age、no-store(不緩存)
Expires(緩存的過(guò)期策略):指名了緩存數(shù)據(jù)有效的絕對(duì)時(shí)間舆声,告訴客戶端到了這個(gè)時(shí)間點(diǎn)(比照客戶端時(shí)間點(diǎn))后本地緩存就作廢了花沉。
如果緩存過(guò)期再去請(qǐng)求服務(wù)器時(shí),不一定拿到數(shù)據(jù)(304)

Http狀態(tài)碼

1xx: Infomational (信息狀態(tài)碼) 媳握,接收的請(qǐng)求正在處理
2xx: Succeed(成功)碱屁,請(qǐng)求正常處理完畢,如 200,204
3xx: Redirection(重定向)蛾找,需要進(jìn)行附加操作娩脾,一般是沒(méi)有響應(yīng)數(shù)據(jù)返回的,如 304(Not,modified)307
4xx: Client Error (客戶端的錯(cuò)誤)打毛,服務(wù)器無(wú)法處理請(qǐng)求柿赊,如 404俩功,405
5xx: Server Error (服務(wù)端的錯(cuò)誤),服務(wù)器處理請(qǐng)求出錯(cuò)碰声,如 500诡蜓,502

Http和Https的區(qū)別

https=http+加密驗(yàn)證

http的缺點(diǎn):
1.數(shù)據(jù)沒(méi)有加密傳輸,可能被監(jiān)聽
2.不驗(yàn)證通信方的身份容易被偽裝
3.無(wú)法驗(yàn)證報(bào)文的完成性可能被篡改

TLS/SSL協(xié)議:
加密:對(duì)稱加密(AES,DES)+非對(duì)稱加密(RSA,DSA)
證書:建立連接的速度會(huì)被拖慢奥邮,TCP 8次握手

Http1.0和Http2.0的區(qū)別

http2.0:
1.使用二進(jìn)制傳輸不是文本
2.可以多路復(fù)用
3.報(bào)頭使用了壓縮
4.讓服務(wù)器可以主動(dòng)推送到客戶端

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末万牺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子洽腺,更是在濱河造成了極大的恐慌脚粟,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蘸朋,死亡現(xiàn)場(chǎng)離奇詭異核无,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)藕坯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門团南,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人炼彪,你說(shuō)我怎么就攤上這事吐根。” “怎么了辐马?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵拷橘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我喜爷,道長(zhǎng)冗疮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任檩帐,我火速辦了婚禮术幔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘湃密。我一直安慰自己诅挑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布泛源。 她就那樣靜靜地躺著揍障,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俩由。 梳的紋絲不亂的頭發(fā)上毒嫡,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼兜畸。 笑死努释,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咬摇。 我是一名探鬼主播伐蒂,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肛鹏!你這毒婦竟也來(lái)了逸邦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤在扰,失蹤者是張志新(化名)和其女友劉穎缕减,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芒珠,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桥狡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了皱卓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裹芝。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖娜汁,靈堂內(nèi)的尸體忽然破棺而出嫂易,到底是詐尸還是另有隱情,我是刑警寧澤掐禁,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布怜械,位于F島的核電站,受9級(jí)特大地震影響穆桂,放射性物質(zhì)發(fā)生泄漏宫盔。R本人自食惡果不足惜融虽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一享完、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧有额,春花似錦般又、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至萤衰,卻和暖如春堕义,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脆栋。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工倦卖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洒擦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓怕膛,卻偏偏與公主長(zhǎng)得像熟嫩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子褐捻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 本文為本人原創(chuàng)掸茅,轉(zhuǎn)載請(qǐng)注明作者和出處。 在上一章我們分析了Okhttp分發(fā)器對(duì)同步/異步請(qǐng)求的處理柠逞,本章將和大家一...
    業(yè)松閱讀 974評(píng)論 2 8
  • 2.okhttp3.0整體流程:1).創(chuàng)建okhttpclient客戶端對(duì)象昧狮,表示所有的http請(qǐng)求的客戶端的類,...
    無(wú)為3閱讀 379評(píng)論 0 1
  • 那么我今天給大家簡(jiǎn)單地講一下Okhttp這款網(wǎng)絡(luò)框架及其原理边苹。它是如何請(qǐng)求數(shù)據(jù)陵且,如何響應(yīng)數(shù)據(jù)的 有什么優(yōu)點(diǎn)?它的應(yīng)...
    卓而不群_0137閱讀 315評(píng)論 0 1
  • 第七章:走進(jìn)佛家(佛XX) 上一篇个束,說(shuō)到我脫口而出讓奶奶(我婆婆)回老家慕购,換爺爺(就是我公公)來(lái)。真的茬底,換爺爺來(lái)了...
    娟姐的心語(yǔ)話廊閱讀 326評(píng)論 4 6
  • 2020年的第一場(chǎng)“旅行”沪悲,是在Peggy的軟磨硬泡中達(dá)成的。她形容整個(gè)游說(shuō)我的過(guò)程是這樣的:花了十多天密集的給我...
    艾筱薇閱讀 171評(píng)論 0 1