如對(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)行了一層封裝。
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ù)鏈路層纪隙、物理層
TCP的三次握手和四次分手
文字總結(jié):
三次握手之后才開始發(fā)送數(shù)據(jù)
客戶端先發(fā)一條信息給服務(wù)端說(shuō)能聽到嗎
服務(wù)端就發(fā)送一條信息并帶ACK給客戶端說(shuō)我能聽到,你能聽到嗎
客戶端就發(fā)送一條信息給服務(wù)端說(shuō)我能聽到
四次揮手:
掛了
好的扛或。掛了
好的
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首部
請(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)推送到客戶端