1、網(wǎng)絡體系結構
| TCP/IP 體系結構 | 五層體系結構 |
|---|---|
| 5.應用層 | |
| 4.應用層(HTTP) | 4.運輸層 |
| 3.運輸層(TCP、UDP) | 3.網(wǎng)絡層 |
| 2.網(wǎng)際層(IP) | 2.鏈路層 |
| 1.網(wǎng)絡接口層 | 1.物理層 |
其中 我們熟悉的 HTTP DNS POP FTP SSH 等協(xié)議,都存在于 應用層
TCP
特點
面向連接、面向字節(jié)流、全雙工通信、可靠
優(yōu)缺點
優(yōu)點:數(shù)據(jù)傳輸可靠
缺點:效率慢(因需建立連接、發(fā)送確認包等)
建立連接
TCP的三次握手
第一次握手:客戶端向服務器發(fā)送一個連接請求的報文段
第二次握手:服務器收到請求連接報文段后,若同意建立連接,則向客戶端發(fā)回連接確認的報文段
第三次握手:大客戶收到確認報文段后,向服務器再次發(fā)出連接確認報文段
注:三次握手期間,任何一次未收到對面的回復,則都會重發(fā)
成功進行TCP的三次握手后,就建立起一條TCP連接,即可傳送應用層數(shù)據(jù)
為什么TCP建立要三次握手
結論:防止服務器端因接收了早已失效的連接請求報文,從而一直等待客戶端請求,最終導致形成死鎖、浪費資源
釋放連接
TCP的四次握手
第一次握手:客戶端向服務器發(fā)送一個連接釋放的報文段
第二次握手:服務器收到連接釋放報文段后,則向客戶端發(fā)回連接釋放確認的報文段(至此,TCP連接處半關閉狀態(tài),即 客戶端->服務器TCP已斷開, 服務器-> 客戶端未斷開)
第三次握手:若服務器已無要向客戶端發(fā)送數(shù)據(jù),則發(fā)出釋放連接的報文段(服務器進入最后確認狀態(tài))
第四次握手:客戶端收到連接釋放報文段后,向服務器發(fā)回連接釋放確認的報文段(經過2MSL后,客戶端進入關閉狀態(tài),服務器比客戶端先關閉)
注:MSL=最長報文段壽命(Maximum Segment Lifetime)
為什么TCP釋放要四次握手
結論:為了保證通信雙方都能通知對方 需釋放 & 斷開連接
HTTP
是基于TCP的應用層協(xié)議
請求
請求行 、請求頭(header)、請求體(
可選,GET 可無請求數(shù)據(jù))
響應
響應行 、響應頭(header)、響應體
GET POST 區(qū)別
| 使用方式 | 傳參長度限制 | 傳參類型 | 安全性 | 應用場景 |
|---|---|---|---|---|
| GET | 最長2048個字符http1.1后無限制
|
只允許ASCII 字符 | 差 | 小量、數(shù)據(jù)不敏感 |
| POST | 不受限制 | 任何類型 | 好 | 大量、數(shù)據(jù)敏感 |
HTTP1.1 與 HTTP1.0的區(qū)別
緩存處理:HTTP/1.0 使用 Pragma:no-cache + Last-Modified/If-Modified-Since來作為緩存判斷的標準;HTTP/1.1 引入了更多的緩存控制策略:Cache-Control、Etag/If-None-Match等。
錯誤狀態(tài)管理:HTTP/1.1新增了24個錯誤狀態(tài)響應碼,如409(Conflict)表示請求的資源與資源的當前狀態(tài)發(fā)生沖突;410(Gone)表示服務器上的某個資源被永久性的刪除。
范圍請求:HTTP/1.1在請求頭引入了range頭域,它允許只請求資源的某個部分,即返回碼是206(Partial Content),這樣就方便了開發(fā)者自由的選擇以便于充分利用帶寬和連接,支持斷點續(xù)傳。
Host頭:HTTP1.0中認為每臺服務器都綁定一個唯一的IP地址,因此,請求消息中的URL并沒有傳遞主機名(hostname)。但隨著虛擬主機技術的發(fā)展,在一臺物理服務器上可以存在多個虛擬主機(Multi-homed Web Servers),并且它們共享一個IP地址。HTTP1.1的請求消息和響應消息都應支持Host頭域,且請求消息中如果沒有Host頭域會報告一個錯誤(400 Bad Request)。有了Host字段,就可以將請求發(fā)往同一臺服務器上的不同網(wǎng)站,為虛擬主機的興起打下了基礎。
持久連接:HTTP/1.1 最大的變化就是引入了持久連接(persistent connection),在HTTP/1.1中默認開啟 Connection: keep-alive,即TCP連接默認不關閉,可以被多個請求復用。
管道機制:HTTP/1.1中引入了管道機制(pipelining),即在同一個TCP連接中,客戶端可以同時發(fā)送多個請求。
HTTP1.1 與 HTTP2.0的區(qū)別
Http2.0是以Google發(fā)布的SPDY協(xié)議為基礎的。HTTP/2協(xié)議只在HTTPS環(huán)境下才有效,升級到HTTP/2,必須先啟用HTTPS。HTTP/2解決了HTTP/1.1的性能問題,主要如下:
二進制分幀:HTTP/1.1的頭信息是文本(ASCII編碼),數(shù)據(jù)體可以是文本,也可以是二進制;HTTP/2 頭信息和數(shù)據(jù)體都是二進制,統(tǒng)稱為“幀”:頭信息幀和數(shù)據(jù)幀;
多路復用(雙工通信):通過單一的 HTTP/2 連接發(fā)起多重的請求-響應消息,即在一個連接里,客戶端和瀏覽器都可以同時發(fā)送多個請求和響應,而不用按照順序一一對應,這樣避免了“隊頭堵塞”。HTTP/2 把 HTTP 協(xié)議通信的基本單位縮小為一個一個的幀,這些幀對應著邏輯流中的消息。并行地在同一個 TCP 連接上雙向交換消息。
數(shù)據(jù)流:因為 HTTP/2 的數(shù)據(jù)包是不按順序發(fā)送的,同一個連接里面連續(xù)的數(shù)據(jù)包,可能屬于不同的回應。因此,必須要對數(shù)據(jù)包做標記,指出它屬于哪個回應。HTTP/2 將每個請求或回應的所有數(shù)據(jù)包,稱為一個數(shù)據(jù)流(stream)。每個數(shù)據(jù)流都有一個獨一無二的編號。數(shù)據(jù)包發(fā)送的時候,都必須標記數(shù)據(jù)流ID,用來區(qū)分它屬于哪個數(shù)據(jù)流。另外還規(guī)定,客戶端發(fā)出的數(shù)據(jù)流,ID一律為奇數(shù),服務器發(fā)出的,ID為偶數(shù)。數(shù)據(jù)流發(fā)送到一半的時候,客戶端和服務器都可以發(fā)送信號(RST_STREAM幀),取消這個數(shù)據(jù)流。HTTP/1.1取消數(shù)據(jù)流的唯一方法,就是關閉TCP連接。這就是說,HTTP/2 可以取消某一次請求,同時保證TCP連接還打開著,可以被其他請求使用。客戶端還可以指定數(shù)據(jù)流的優(yōu)先級。優(yōu)先級越高,服務器就會越早回應。
頭部壓縮:HTTP 協(xié)議不帶有狀態(tài),每次請求都必須附上所有信息。所以,請求的很多字段都是重復的,,一模一樣的內容,每次請求都必須附帶,這會浪費很多帶寬,也影響速度。HTTP/2 對這一點做了優(yōu)化,引入了頭信息壓縮機制(header compression)。一方面,頭信息壓縮后再發(fā)送(SPDY 使用的是通用的DEFLATE 算法,而 HTTP/2 則使用了專門為首部壓縮而設計的 HPACK 算法)。;另一方面,客戶端和服務器同時維護一張頭信息表,所有字段都會存入這個表,生成一個索引號,以后就不發(fā)送同樣字段了,只發(fā)送索引號,這樣就提高速度了。
服務端推送:HTTP/2 允許服務器未經請求,主動向客戶端發(fā)送資源,這叫做服務器推送(server push)。常見場景是客戶端請求一個網(wǎng)頁,這個網(wǎng)頁里面包含很多靜態(tài)資源。正常情況下,客戶端必須收到網(wǎng)頁后,解析HTML源碼,發(fā)現(xiàn)有靜態(tài)資源,再發(fā)出靜態(tài)資源請求。其實,服務器可以預期到客戶端請求網(wǎng)頁后,很可能會再請求靜態(tài)資源,所以就主動把這些靜態(tài)資源隨著網(wǎng)頁一起發(fā)給客戶端了。
HTTP 與 HTTPS的區(qū)別
| 類型 | 原理 | 功能 | 性能 | 標準端口 | CA申請證書 |
|---|---|---|---|---|---|
| HTTP | 應用層 | 不加密 | 不安全 | 80 | 不要 |
| HTTPS | 傳輸層 | 加密SSL加密 身份認證
|
安全 | 443 | 需要 |
HTTPS加密原理
HTTPS = HTTP + SSL/TLS(安全套接層Secure Sockets Layer/安全傳輸層Transport Layer Security)。也就是在傳統(tǒng)的HTTP和TCP之間加了一層用于加密解密的SSL/TLS層。
一篇讀懂HTTPS
Socket
套接字,是應用層 與 TCP/IP 協(xié)議通信的中間軟件抽象層,表現(xiàn)為一個封裝了TCP/IP協(xié)議的編程接口
- Socket不是一種協(xié)議,而是一個編程調用接口(API),屬于傳輸層(主要解決數(shù)據(jù)如何在網(wǎng)絡中傳輸)
- 通過Socket,我們才能在Andorid平臺上通過 TCP/IP協(xié)議進行開發(fā)
- 對用戶來說,只需調用Socket去組織數(shù)據(jù),以符合指定的協(xié)議,即可通信
2、Retrofit 2
Retrofit 本質上是一個 RESTful 的 HTTP 網(wǎng)絡請求框架的封裝,即通過 大量的設計模式 封裝了 OkHttp ,使得簡潔易用。具體過程如下:
- Retrofit 將 Http請求 抽象 成 Java接口
- 在接口里用 注解 描述和配置 網(wǎng)絡請求參數(shù)
- 用動態(tài)代理 的方式,動態(tài)將網(wǎng)絡請求接口的注解 解析 成HTTP請求
- 最后執(zhí)行HTTP請求
一張 大神 的源碼分析圖:

3、OKHttp 3
- 支持 HTTP1.1/HTTP2 和 SPDY
- 對同一主機共享同一個socket連接,減少握手次數(shù),提高效率
- 支持Gzip 降低傳輸內容大小
- 支持自動重連
- 擁有Dispatcher線程池,并發(fā)支持
- 擁有Interceptor攔截器及調用鏈,輕松處理請求和響應
OkHttp系統(tǒng)圖

OkHttp 創(chuàng)建了 實例對象
OkHttpClient,支持WebSocket(RealWebSocket)和Call(RealCall)。常使用的 Call 方法,內部是Dispatcher 線程池,實際是 線程池 支持execute (同步)和enqueue(異步)調用。同步 異步 都使用了RealInterceptorChain鏈式調用方式。最后通過StreamAllocation,RealConnection,HttpCodec發(fā)送網(wǎng)絡請求并得到結果
OkHttpClient
OkHttpClient 是 OkHttp 通過 建造者模式 創(chuàng)建的實例對象。
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
......
}
可見,默認支持 http2 和 http1.1,默認使用 TSL 安全協(xié)議。
WebSocket
WebSocket 是一種在TCP協(xié)議上進行的 全雙工通信的協(xié)議,支持服務器想客戶端的發(fā)送請求。
@Override public WebSocket newWebSocket(Request request, WebSocketListener listener) {
RealWebSocket webSocket = new RealWebSocket(request, listener, new Random(), pingInterval);
webSocket.connect(this);
return webSocket;
}
Call(RealCall)
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
Call 的實際操作對象是 RealCall
final class RealCall implements Call {
......
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
}
RealCall 是真正觸發(fā)網(wǎng)絡請求的類(實現(xiàn)Call接口,一次請求 = 一個RealCall實例),它提供了同步請求、異步請求
execute 同步請求
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
client.dispatcher().executed(this)同步調用主要是使用了 dispatcher 線程池的同步方法getResponseWithInterceptorChain ()采用了 鏈式方式 獲取響應
enqueue 異步請求
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));創(chuàng)建了AsyncCall交給 dispatcher線程池 異步處理
final class AsyncCall extends NamedRunnable {
// ......
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
Dispatcher線程池
Dispatcher 管理網(wǎng)絡請求的線程池,就是把同步 RealCall 與異步 RealCall .AsyncCall 的請求放進集合中統(tǒng)一管理。
- RealCall 在Dispatcher中,其實主要就是一個存儲功能(即用一個集合把RealCall的請求進行存儲)。
- AsyncCall 在Dispatcher中,除了使用集合存儲AsyncCall的請求,Dispatcher還初始化了一個線程池(ThreadPoolExecutor)處理AsyncCall的網(wǎng)絡請求。
public final class Dispatcher {
private int maxRequests = 64; //最大請求數(shù)量
private int maxRequestsPerHost = 5; //相同host的最大請求數(shù)據(jù)
private @Nullable Runnable idleCallback;
......
//同步請求
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
// 異步請求
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
}
Interceptor攔截器及調用鏈
同、異步請求中都調用了 getResponseWithInterceptorChain() 方法。
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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
- client.interceptors()
- 添加OkHttp 自定義的 攔截器。如
HttpLoggingInterceptor等- RetryAndFollowUpInterceptor
- 定義:重定向攔截器;
- 作用:在無法請求服務器或者請求失敗時,服務器會告訴客戶端可以處理請求的url,然后重定向攔截器承當重新請求新url的作用(服務器返回3XX錯誤碼為重定向,可以通過響應頭的Location獲取新請求的url);
- BridgeInterceptor
- 定義:橋攔截器;
- 作用:封裝請求頭(Content-Type、Connection、Cookie...)與響應頭("Content-Encoding...)的信息。
- CacheInterceptor
- 定義:緩存攔截器;
- 作用:為網(wǎng)絡請求提供緩存功能,加快相同請求的訪問速度,減少資源損耗。
- ConnectInterceptor
- 定義:連接攔截器;
- 作用:與服務器建立通訊連接。
- CallServerInterceptor
- 定義:請求服務器攔截器;
- 作用:與服務器進行數(shù)據(jù)通訊(包含請求頭、請求內容)。
注:調用鏈采用 *責任鏈* 的方式,向下請求,向上響應。類似 Android View 的事件傳遞
StreamAllocation、RealConnection、HttpCodec 連接與請求
- StreamAllocation :負責初始化 RealConnection、HttpCodec,并將前2者與RealCall進行關聯(lián);
- StreamAllocation 在
RetryAndFollowUpInterceptor中初始化 - RealConnection 在 ConnectInterceptor 中通過 StreamAllocation 的
newStream()初始化 - HttpCodec 在 RealConnection 中被初始化
- RealConnection:真正負責完成網(wǎng)絡連接
connectSocket(connectTimeout, readTimeout, call, eventListener);
最后通過 Socket 進行網(wǎng)絡連接
- HttpCodec:負責完成發(fā)送請求頭與數(shù)據(jù)內容(使用okio完成數(shù)據(jù)的寫入與讀出)
有 Http1Codec 和 Http2Codec,分包處理 HTTP1.1 和 HTTP2.0 的數(shù)據(jù)傳遞