Android 網(wǎng)絡知識梳理

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ū)別

  1. 緩存處理:HTTP/1.0 使用 Pragma:no-cache + Last-Modified/If-Modified-Since來作為緩存判斷的標準;HTTP/1.1 引入了更多的緩存控制策略:Cache-Control、Etag/If-None-Match等。

  2. 錯誤狀態(tài)管理:HTTP/1.1新增了24個錯誤狀態(tài)響應碼,如409(Conflict)表示請求的資源與資源的當前狀態(tài)發(fā)生沖突;410(Gone)表示服務器上的某個資源被永久性的刪除。

  3. 范圍請求:HTTP/1.1在請求頭引入了range頭域,它允許只請求資源的某個部分,即返回碼是206(Partial Content),這樣就方便了開發(fā)者自由的選擇以便于充分利用帶寬和連接,支持斷點續(xù)傳。

  4. Host頭:HTTP1.0中認為每臺服務器都綁定一個唯一的IP地址,因此,請求消息中的URL并沒有傳遞主機名(hostname)。但隨著虛擬主機技術的發(fā)展,在一臺物理服務器上可以存在多個虛擬主機(Multi-homed Web Servers),并且它們共享一個IP地址。HTTP1.1的請求消息和響應消息都應支持Host頭域,且請求消息中如果沒有Host頭域會報告一個錯誤(400 Bad Request)。有了Host字段,就可以將請求發(fā)往同一臺服務器上的不同網(wǎng)站,為虛擬主機的興起打下了基礎。

  5. 持久連接:HTTP/1.1 最大的變化就是引入了持久連接(persistent connection),在HTTP/1.1中默認開啟 Connection: keep-alive,即TCP連接默認不關閉,可以被多個請求復用。

  6. 管道機制: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的性能問題,主要如下:

  1. 二進制分幀:HTTP/1.1的頭信息是文本(ASCII編碼),數(shù)據(jù)體可以是文本,也可以是二進制;HTTP/2 頭信息和數(shù)據(jù)體都是二進制,統(tǒng)稱為“幀”:頭信息幀和數(shù)據(jù)幀;

  2. 多路復用(雙工通信):通過單一的 HTTP/2 連接發(fā)起多重的請求-響應消息,即在一個連接里,客戶端和瀏覽器都可以同時發(fā)送多個請求和響應,而不用按照順序一一對應,這樣避免了“隊頭堵塞”。HTTP/2 把 HTTP 協(xié)議通信的基本單位縮小為一個一個的幀,這些幀對應著邏輯流中的消息。并行地在同一個 TCP 連接上雙向交換消息。

  3. 數(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)先級越高,服務器就會越早回應。

  4. 頭部壓縮:HTTP 協(xié)議不帶有狀態(tài),每次請求都必須附上所有信息。所以,請求的很多字段都是重復的,,一模一樣的內容,每次請求都必須附帶,這會浪費很多帶寬,也影響速度。HTTP/2 對這一點做了優(yōu)化,引入了頭信息壓縮機制(header compression)。一方面,頭信息壓縮后再發(fā)送(SPDY 使用的是通用的DEFLATE 算法,而 HTTP/2 則使用了專門為首部壓縮而設計的 HPACK 算法)。;另一方面,客戶端和服務器同時維護一張頭信息表,所有字段都會存入這個表,生成一個索引號,以后就不發(fā)送同樣字段了,只發(fā)送索引號,這樣就提高速度了。

  5. 服務端推送: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 ,使得簡潔易用。具體過程如下:

  1. Retrofit 將 Http請求 抽象 成 Java接口
  2. 在接口里用 注解 描述和配置 網(wǎng)絡請求參數(shù)
  3. 用動態(tài)代理 的方式,動態(tài)將網(wǎng)絡請求接口的注解 解析 成HTTP請求
  4. 最后執(zhí)行HTTP請求

一張 大神 的源碼分析圖:

源碼分析圖

3、OKHttp 3

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

OkHttp系統(tǒng)圖

系統(tǒng)圖

OkHttp 創(chuàng)建了 實例對象 OkHttpClient ,支持 WebSocket(RealWebSocket)Call(RealCall) 。常使用的 Call 方法,內部是 Dispatcher 線程池 ,實際是 線程池 支持 execute (同步)enqueue(異步) 調用。同步 異步 都使用了 RealInterceptorChain 鏈式調用方式。最后通過 StreamAllocation,RealConnectionHttpCodec 發(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);

    ......
}

可見,默認支持 http2http1.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);
    }
  }
  1. client.dispatcher().executed(this) 同步調用主要是使用了 dispatcher 線程池的同步方法
  2. 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);
    }
  1. client.interceptors()
    • 添加OkHttp 自定義的 攔截器。如 HttpLoggingInterceptor
  2. RetryAndFollowUpInterceptor
    • 定義:重定向攔截器;
    • 作用:在無法請求服務器或者請求失敗時,服務器會告訴客戶端可以處理請求的url,然后重定向攔截器承當重新請求新url的作用(服務器返回3XX錯誤碼為重定向,可以通過響應頭的Location獲取新請求的url);
  3. BridgeInterceptor
    • 定義:橋攔截器;
    • 作用:封裝請求頭(Content-Type、Connection、Cookie...)與響應頭("Content-Encoding...)的信息。
  4. CacheInterceptor
    • 定義:緩存攔截器;
    • 作用:為網(wǎng)絡請求提供緩存功能,加快相同請求的訪問速度,減少資源損耗。
  5. ConnectInterceptor
    • 定義:連接攔截器;
    • 作用:與服務器建立通訊連接。
  6. CallServerInterceptor
    • 定義:請求服務器攔截器;
    • 作用:與服務器進行數(shù)據(jù)通訊(包含請求頭、請求內容)。

注:調用鏈采用 *責任鏈* 的方式,向下請求,向上響應。類似 Android View 的事件傳遞

StreamAllocation、RealConnection、HttpCodec 連接與請求

  1. StreamAllocation :負責初始化 RealConnection、HttpCodec,并將前2者與RealCall進行關聯(lián);
  • StreamAllocation 在 RetryAndFollowUpInterceptor 中初始化
  • RealConnection 在 ConnectInterceptor 中通過 StreamAllocation 的 newStream() 初始化
  • HttpCodec 在 RealConnection 中被初始化
  1. RealConnection:真正負責完成網(wǎng)絡連接
connectSocket(connectTimeout, readTimeout, call, eventListener); 

最后通過 Socket 進行網(wǎng)絡連接

  1. HttpCodec:負責完成發(fā)送請求頭與數(shù)據(jù)內容(使用okio完成數(shù)據(jù)的寫入與讀出)

Http1CodecHttp2Codec,分包處理 HTTP1.1 和 HTTP2.0 的數(shù)據(jù)傳遞

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容