1、網(wǎng)絡(luò)體系結(jié)構(gòu)
TCP/IP 體系結(jié)構(gòu) | 五層體系結(jié)構(gòu) |
---|---|
5.應(yīng)用層 | |
4.應(yīng)用層(HTTP) | 4.運(yùn)輸層 |
3.運(yùn)輸層(TCP捐友、UDP) | 3.網(wǎng)絡(luò)層 |
2.網(wǎng)際層(IP) | 2.鏈路層 |
1.網(wǎng)絡(luò)接口層 | 1.物理層 |
其中 我們熟悉的 HTTP
DNS
POP
FTP
SSH
等協(xié)議,都存在于 應(yīng)用層
TCP
特點
面向連接焚虱、面向字節(jié)流基矮、全雙工通信聚磺、可靠
優(yōu)缺點
優(yōu)點:數(shù)據(jù)傳輸可靠
缺點:效率慢(因需建立連接、發(fā)送確認(rèn)包等)
建立連接
TCP的三次握手
第一次握手:客戶端向服務(wù)器發(fā)送一個連接請求的報文段
第二次握手:服務(wù)器收到請求連接報文段后聂沙,若同意建立連接秆麸,則向客戶端發(fā)回連接確認(rèn)的報文段
第三次握手:大客戶收到確認(rèn)報文段后,向服務(wù)器再次發(fā)出連接確認(rèn)報文段
注:三次握手期間及汉,任何一次未收到對面的回復(fù)沮趣,則都會重發(fā)
成功進(jìn)行TCP的三次握手后,就建立起一條TCP連接坷随,即可傳送應(yīng)用層數(shù)據(jù)
為什么TCP建立要三次握手
結(jié)論:防止服務(wù)器端因接收了早已失效的連接請求報文房铭,從而一直等待客戶端請求,最終導(dǎo)致形成死鎖温眉、浪費資源
釋放連接
TCP的四次握手
第一次握手:客戶端向服務(wù)器發(fā)送一個連接釋放的報文段
第二次握手:服務(wù)器收到連接釋放報文段后缸匪,則向客戶端發(fā)回連接釋放確認(rèn)的報文段(至此,TCP連接處半關(guān)閉狀態(tài)类溢,即 客戶端->服務(wù)器TCP已斷開凌蔬, 服務(wù)器-> 客戶端未斷開
)
第三次握手:若服務(wù)器已無要向客戶端發(fā)送數(shù)據(jù),則發(fā)出釋放連接的報文段(服務(wù)器進(jìn)入最后確認(rèn)狀態(tài)
)
第四次握手:客戶端收到連接釋放報文段后闯冷,向服務(wù)器發(fā)回連接釋放確認(rèn)的報文段(經(jīng)過2MSL后砂心,客戶端進(jìn)入關(guān)閉狀態(tài),服務(wù)器比客戶端先關(guān)閉
)
注:MSL=最長報文段壽命(Maximum Segment Lifetime)
為什么TCP釋放要四次握手
結(jié)論:為了保證通信雙方都能通知對方 需釋放 & 斷開連接
HTTP
是基于TCP的應(yīng)用層協(xié)議
請求
請求行 蛇耀、請求頭(header)辩诞、請求體(
可選,GET 可無請求數(shù)據(jù)
)
響應(yīng)
響應(yīng)行 纺涤、響應(yīng)頭(header)译暂、響應(yīng)體
GET POST 區(qū)別
使用方式 | 傳參長度限制 | 傳參類型 | 安全性 | 應(yīng)用場景 |
---|---|---|---|---|
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來作為緩存判斷的標(biāo)準(zhǔn)秧秉;HTTP/1.1 引入了更多的緩存控制策略:Cache-Control褐桌、Etag/If-None-Match等。
錯誤狀態(tài)管理:HTTP/1.1新增了24個錯誤狀態(tài)響應(yīng)碼象迎,如409(Conflict)表示請求的資源與資源的當(dāng)前狀態(tài)發(fā)生沖突荧嵌;410(Gone)表示服務(wù)器上的某個資源被永久性的刪除。
范圍請求:HTTP/1.1在請求頭引入了range頭域砾淌,它允許只請求資源的某個部分啦撮,即返回碼是206(Partial Content),這樣就方便了開發(fā)者自由的選擇以便于充分利用帶寬和連接汪厨,支持?jǐn)帱c續(xù)傳赃春。
Host頭:HTTP1.0中認(rèn)為每臺服務(wù)器都綁定一個唯一的IP地址,因此劫乱,請求消息中的URL并沒有傳遞主機(jī)名(hostname)织中。但隨著虛擬主機(jī)技術(shù)的發(fā)展,在一臺物理服務(wù)器上可以存在多個虛擬主機(jī)(Multi-homed Web Servers)衷戈,并且它們共享一個IP地址狭吼。HTTP1.1的請求消息和響應(yīng)消息都應(yīng)支持Host頭域,且請求消息中如果沒有Host頭域會報告一個錯誤(400 Bad Request)殖妇。有了Host字段刁笙,就可以將請求發(fā)往同一臺服務(wù)器上的不同網(wǎng)站,為虛擬主機(jī)的興起打下了基礎(chǔ)谦趣。
持久連接:HTTP/1.1 最大的變化就是引入了持久連接(persistent connection)疲吸,在HTTP/1.1中默認(rèn)開啟 Connection: keep-alive,即TCP連接默認(rèn)不關(guān)閉前鹅,可以被多個請求復(fù)用摘悴。
管道機(jī)制:HTTP/1.1中引入了管道機(jī)制(pipelining),即在同一個TCP連接中,客戶端可以同時發(fā)送多個請求舰绘。
HTTP1.1 與 HTTP2.0的區(qū)別
Http2.0是以Google發(fā)布的SPDY協(xié)議為基礎(chǔ)的烦租。HTTP/2協(xié)議只在HTTPS環(huán)境下才有效,升級到HTTP/2除盏,必須先啟用HTTPS。HTTP/2解決了HTTP/1.1的性能問題挫以,主要如下:
二進(jìn)制分幀:HTTP/1.1的頭信息是文本(ASCII編碼)者蠕,數(shù)據(jù)體可以是文本,也可以是二進(jìn)制掐松;HTTP/2 頭信息和數(shù)據(jù)體都是二進(jìn)制踱侣,統(tǒng)稱為“幀”:頭信息幀和數(shù)據(jù)幀粪小;
多路復(fù)用(雙工通信):通過單一的 HTTP/2 連接發(fā)起多重的請求-響應(yīng)消息,即在一個連接里抡句,客戶端和瀏覽器都可以同時發(fā)送多個請求和響應(yīng)探膊,而不用按照順序一一對應(yīng),這樣避免了“隊頭堵塞”待榔。HTTP/2 把 HTTP 協(xié)議通信的基本單位縮小為一個一個的幀逞壁,這些幀對應(yīng)著邏輯流中的消息。并行地在同一個 TCP 連接上雙向交換消息锐锣。
數(shù)據(jù)流:因為 HTTP/2 的數(shù)據(jù)包是不按順序發(fā)送的腌闯,同一個連接里面連續(xù)的數(shù)據(jù)包,可能屬于不同的回應(yīng)雕憔。因此姿骏,必須要對數(shù)據(jù)包做標(biāo)記,指出它屬于哪個回應(yīng)斤彼。HTTP/2 將每個請求或回應(yīng)的所有數(shù)據(jù)包分瘦,稱為一個數(shù)據(jù)流(stream)。每個數(shù)據(jù)流都有一個獨一無二的編號琉苇。數(shù)據(jù)包發(fā)送的時候嘲玫,都必須標(biāo)記數(shù)據(jù)流ID,用來區(qū)分它屬于哪個數(shù)據(jù)流翁潘。另外還規(guī)定趁冈,客戶端發(fā)出的數(shù)據(jù)流,ID一律為奇數(shù)拜马,服務(wù)器發(fā)出的渗勘,ID為偶數(shù)。數(shù)據(jù)流發(fā)送到一半的時候俩莽,客戶端和服務(wù)器都可以發(fā)送信號(RST_STREAM幀)旺坠,取消這個數(shù)據(jù)流。HTTP/1.1取消數(shù)據(jù)流的唯一方法扮超,就是關(guān)閉TCP連接取刃。這就是說,HTTP/2 可以取消某一次請求出刷,同時保證TCP連接還打開著璧疗,可以被其他請求使用∧俟辏客戶端還可以指定數(shù)據(jù)流的優(yōu)先級崩侠。優(yōu)先級越高,服務(wù)器就會越早回應(yīng)坷檩。
頭部壓縮:HTTP 協(xié)議不帶有狀態(tài)却音,每次請求都必須附上所有信息改抡。所以,請求的很多字段都是重復(fù)的系瓢,阿纤,一模一樣的內(nèi)容,每次請求都必須附帶夷陋,這會浪費很多帶寬欠拾,也影響速度。HTTP/2 對這一點做了優(yōu)化肌稻,引入了頭信息壓縮機(jī)制(header compression)清蚀。一方面,頭信息壓縮后再發(fā)送(SPDY 使用的是通用的DEFLATE 算法爹谭,而 HTTP/2 則使用了專門為首部壓縮而設(shè)計的 HPACK 算法)枷邪。;另一方面诺凡,客戶端和服務(wù)器同時維護(hù)一張頭信息表东揣,所有字段都會存入這個表,生成一個索引號腹泌,以后就不發(fā)送同樣字段了嘶卧,只發(fā)送索引號,這樣就提高速度了凉袱。
服務(wù)端推送:HTTP/2 允許服務(wù)器未經(jīng)請求芥吟,主動向客戶端發(fā)送資源,這叫做服務(wù)器推送(server push)专甩。常見場景是客戶端請求一個網(wǎng)頁钟鸵,這個網(wǎng)頁里面包含很多靜態(tài)資源。正常情況下涤躲,客戶端必須收到網(wǎng)頁后棺耍,解析HTML源碼,發(fā)現(xiàn)有靜態(tài)資源种樱,再發(fā)出靜態(tài)資源請求蒙袍。其實,服務(wù)器可以預(yù)期到客戶端請求網(wǎng)頁后嫩挤,很可能會再請求靜態(tài)資源害幅,所以就主動把這些靜態(tài)資源隨著網(wǎng)頁一起發(fā)給客戶端了。
HTTP 與 HTTPS的區(qū)別
類型 | 原理 | 功能 | 性能 | 標(biāo)準(zhǔn)端口 | CA申請證書 |
---|---|---|---|---|---|
HTTP | 應(yīng)用層 | 不加密 | 不安全 | 80 | 不要 |
HTTPS | 傳輸層 | 加密SSL加密 身份認(rèn)證
|
安全 | 443 | 需要 |
HTTPS加密原理
HTTPS = HTTP + SSL/TLS(安全套接層Secure Sockets Layer/安全傳輸層Transport Layer Security)岂昭。也就是在傳統(tǒng)的HTTP和TCP之間加了一層用于加密解密的SSL/TLS層霜瘪。
一篇讀懂HTTPS
Socket
套接字岭妖,是應(yīng)用層 與 TCP/IP 協(xié)議通信的中間軟件抽象層涧狮,表現(xiàn)為一個封裝了TCP/IP協(xié)議的編程接口
- Socket不是一種協(xié)議帖世,而是一個編程調(diào)用接口(API),屬于傳輸層(主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸)
- 通過Socket棍苹,我們才能在Andorid平臺上通過 TCP/IP協(xié)議進(jìn)行開發(fā)
- 對用戶來說无宿,只需調(diào)用Socket去組織數(shù)據(jù),以符合指定的協(xié)議枢里,即可通信
2孽鸡、Retrofit 2
Retrofit 本質(zhì)上是一個 RESTful
的 HTTP 網(wǎng)絡(luò)請求框架的封裝,即通過 大量的設(shè)計模式 封裝了 OkHttp 栏豺,使得簡潔易用彬碱。具體過程如下:
- Retrofit 將 Http請求 抽象 成 Java接口
- 在接口里用 注解 描述和配置 網(wǎng)絡(luò)請求參數(shù)
- 用動態(tài)代理 的方式,動態(tài)將網(wǎng)絡(luò)請求接口的注解 解析 成HTTP請求
- 最后執(zhí)行HTTP請求
一張 大神
的源碼分析圖:
3奥洼、OKHttp 3
- 支持 HTTP1.1/HTTP2 和 SPDY
- 對同一主機(jī)共享同一個socket連接巷疼,減少握手次數(shù),提高效率
- 支持Gzip 降低傳輸內(nèi)容大小
- 支持自動重連
- 擁有Dispatcher線程池灵奖,并發(fā)支持
- 擁有Interceptor攔截器及調(diào)用鏈嚼沿,輕松處理請求和響應(yīng)
OkHttp系統(tǒng)圖
OkHttp 創(chuàng)建了 實例對象
OkHttpClient
,支持WebSocket(RealWebSocket)
和Call(RealCall)
瓷患。常使用的 Call 方法骡尽,內(nèi)部是Dispatcher 線程池
,實際是 線程池 支持execute (同步)
和enqueue(異步)
調(diào)用。同步 異步 都使用了RealInterceptorChain
鏈?zhǔn)秸{(diào)用方式擅编。最后通過StreamAllocation
攀细,RealConnection
,HttpCodec
發(fā)送網(wǎng)絡(luò)請求并得到結(jié)果
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);
......
}
可見谭贪,默認(rèn)支持 http2
和 http1.1
,默認(rèn)使用 TSL
安全協(xié)議肢藐。
WebSocket
WebSocket 是一種在TCP協(xié)議上進(jìn)行的 全雙工通信的協(xié)議
故河,支持服務(wù)器想客戶端的發(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)絡(luò)請求的類(實現(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)
同步調(diào)用主要是使用了 dispatcher 線程池的同步方法getResponseWithInterceptorChain ()
采用了 鏈?zhǔn)椒绞?獲取響應(yīng)
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)絡(luò)請求的線程池痘煤,就是把同步 RealCall
與異步 RealCall .AsyncCall
的請求放進(jìn)集合中統(tǒng)一管理凑阶。
- RealCall 在Dispatcher中,其實主要就是一個存儲功能(即用一個集合把RealCall的請求進(jìn)行存儲)衷快。
- AsyncCall 在Dispatcher中宙橱,除了使用集合存儲AsyncCall的請求,Dispatcher還初始化了一個線程池(ThreadPoolExecutor)處理AsyncCall的網(wǎng)絡(luò)請求。
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攔截器及調(diào)用鏈
同师郑、異步請求中都調(diào)用了 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
- 定義:重定向攔截器宝冕;
- 作用:在無法請求服務(wù)器或者請求失敗時张遭,服務(wù)器會告訴客戶端可以處理請求的url,然后重定向攔截器承當(dāng)重新請求新url的作用(服務(wù)器返回3XX錯誤碼為重定向地梨,可以通過響應(yīng)頭的Location獲取新請求的url);
- BridgeInterceptor
- 定義:橋攔截器菊卷;
- 作用:封裝請求頭(Content-Type、Connection宝剖、Cookie...)與響應(yīng)頭("Content-Encoding...)的信息洁闰。
- CacheInterceptor
- 定義:緩存攔截器;
- 作用:為網(wǎng)絡(luò)請求提供緩存功能万细,加快相同請求的訪問速度扑眉,減少資源損耗。
- ConnectInterceptor
- 定義:連接攔截器雅镊;
- 作用:與服務(wù)器建立通訊連接襟雷。
- CallServerInterceptor
- 定義:請求服務(wù)器攔截器;
- 作用:與服務(wù)器進(jìn)行數(shù)據(jù)通訊(包含請求頭仁烹、請求內(nèi)容)耸弄。
注:調(diào)用鏈采用 *責(zé)任鏈* 的方式,向下請求卓缰,向上響應(yīng)计呈。類似 Android View 的事件傳遞
StreamAllocation、RealConnection征唬、HttpCodec 連接與請求
- StreamAllocation :負(fù)責(zé)初始化 RealConnection捌显、HttpCodec,并將前2者與RealCall進(jìn)行關(guān)聯(lián)总寒;
- StreamAllocation 在
RetryAndFollowUpInterceptor
中初始化 - RealConnection 在 ConnectInterceptor 中通過 StreamAllocation 的
newStream()
初始化 - HttpCodec 在 RealConnection 中被初始化
- RealConnection:真正負(fù)責(zé)完成網(wǎng)絡(luò)連接
connectSocket(connectTimeout, readTimeout, call, eventListener);
最后通過 Socket
進(jìn)行網(wǎng)絡(luò)連接
- HttpCodec:負(fù)責(zé)完成發(fā)送請求頭與數(shù)據(jù)內(nèi)容(使用okio完成數(shù)據(jù)的寫入與讀出)
有 Http1Codec
和 Http2Codec
扶歪,分包處理 HTTP1.1 和 HTTP2.0 的數(shù)據(jù)傳遞