導航:首頁 > 源碼編譯 > okhttp源碼使用

okhttp源碼使用

發布時間:2024-08-21 18:50:27

㈠ OkHttp源碼解析 (三)——代理和路由

初看OkHttp源碼,由於對Address、Route、Proxy、ProxySelector、RouteSelector等理解不夠,讀源碼非常吃力,看了幾遍依然對於尋找復用連接、創建連接、連接伺服器、連接代理伺服器、創建隧道連接等邏輯似懂非懂,本篇決定梳理一遍相關的概念及基本原理。

● HTTP/1.1(HTTPS)
● HTTP/2
● SPDY

一個http請求的流程(直連):
1、輸入url及參數;
2、如果是url是域名則解析ip地址,可能對應多個ip,如果沒有指定埠,則用默認埠,http請求用80;
3、創建socket,根據ip和埠連接伺服器(socket內部會完成3次TCP握手);
4、socket成功連接後,發送http報文數據。

一個https請求的流程(直連):
1、輸入url及參數;
2、如果是url是域名則解析ip地址,可能對應多個ip,如果沒有指定埠,則用默認埠,https請求用443;
3、創建socket,根據ip和埠連接伺服器(socket內部會完成3次TCP握手);
4、socket成功連接後進行TLS握手,可通過java標准款提供的SSLSocket完成;
5、握手成功後,發送https報文數據。

1、分類
● HTTP代理:普通代理、隧道代理
● SOCKS代理:SOCKS4、SOCKS5

2、HTTP代理分類及說明
普通代理
HTTP/1.1 協議的第一部分。其代理過程為:
● client 請求 proxy
● proxy 解析請求獲取 origin server 地址
● proxy 向 origin server 轉發請求
● proxy 接收 origin server 的響應
● proxy 向 client 轉發響應
其中proxy獲取目的伺服器地址的標准方法是解析 request line 里的 request-URL。因為proxy需要解析報文,因此普通代理無法適用於https,因為報文都是加密的。

隧道代理
通過 Web 代理伺服器用隧道方式傳輸基於 TCP 的協議。
請求包括兩個階段,一是連接(隧道)建立階段,二是數據通信(請求響應)階段,數據通信是基於 TCP packet ,代理伺服器不會對請求及響應的報文作任何的處理,都是原封不動的轉發,因此可以代理 HTTPS請求和響應。
代理過程為:
● client 向 proxy 發送 CONNET 請求(包含了 origin server 的地址)
● proxy 與 origin server 建立 TCP 連接
● proxy 向 client 發送響應
● client 向 proxy 發送請求,proxy 原封不動向 origin server 轉發請求,請求數據不做任何封裝,為原生 TCP packet.

3、SOCKS代理分類及說明
● SOCKS4:只支持TCP協議(即傳輸控制協議)
● SOCKS5: 既支持TCP協議又支持UDP協議(即用戶數據包協議),還支持各種身份驗證機制、伺服器端域名解析等。
SOCK4能做到的SOCKS5都可得到,但反過來卻不行,比如我們常用的聊天工具QQ在使用代理時就要求用SOCKS5代理,因為它需要使用UDP協議來傳輸數據。

有了上面的基礎知識,下面分析結合源碼分析OkHttp路由相關的邏輯。OkHttp用Address來描述與目標伺服器建立連接的配置信息,但請求輸入的可能是域名,一個域名可能對於多個ip,真正建立連接是其中一個ip,另外,如果設置了代理,客戶端是與代理伺服器建立直接連接,而不是目標伺服器,代理又可能是域名,可能對應多個ip。因此,這里用Route來描述最終選擇的路由,即客戶端與哪個ip建立連接,是代理還是直連。下面對比下Address及Route的屬性,及路由選擇器RouteSelector。

描述與目標伺服器建立連接所需要的配置信息,包括目標主機名、埠、dns,SocketFactory,如果是https請求,包括TLS相關的SSLSocketFactory 、HostnameVerifier 、CertificatePinner,代理伺服器信息Proxy 、ProxySelector 。

Route提供了真正連接伺服器所需要的動態信息,明確需要連接的伺服器IP地址及代理伺服器,一個Address可能會有很多個路由Route供選擇(一個DNS對應對個IP)。

Address和Route都是數據對象,沒有提供操作方法,OkHttp另外定義了RouteSelector來完成選擇的路由的操作。

1、讀取代理配置信息:resetNextProxy()

讀取代理配置:
● 如果有指定代理(不讀取系統配置,在OkHttpClient實例中指定),則只用1個該指定代理;
● 如果沒有指定,則讀取系統配置的,可能有多個。

2、獲取需要嘗試的socket地址(目標伺服器或者代理伺服器):resetNextInetSocketAddress()

結合Address的host和代理,解析要嘗試的套接字地址(ip+埠)列表:
● 直連或者SOCK代理, 則用目標伺服器的主機名和埠,如果是HTTP代理,則用代理伺服器的主機名和埠;
● 如果是SOCK代理,根據目標伺服器主機名和埠號創建未解析的套接字地址,列表只有1個地址;
● 如果是直連或HTTP代理,先DNS解析,得到InetAddress列表(沒有埠),再創建InetSocketAddress列表(帶上埠),InetSocketAddress與InetAddress的區別是前者帶埠信息。

3、獲取路由列表:next()

選擇路由的流程解析:
● 遍歷每個代理對象,可能多個,直連的代理對象為Proxy.DIRECT(實際是沒有中間代理的);
● 對每個代理獲取套接字地址列表;
● 遍歷地址列表,創建Route,判斷Route如果在路由黑名單中,則添加到失敗路由列表,不在黑名單中則添加到待返回的Route列表;
● 如果最後待返回的Route列表為空,即可能所有路由都在黑名單中,實在沒有新路由了,則將失敗的路由集合返回;
● 傳入Route列表創建Selection對象,對象比較簡單,就是一個目標路由集合,及讀取方法。

為了避免不必要的嘗試,OkHttp會把連接失敗的路由加入到黑名單中,由RouteDatabase管理,該類比較簡單,就是一個失敗路由集合。

1、創建Address
Address的創建在RetryAndFollowUpInteceptor里,每次請求會聲明一個新的Address及StreamAllocation對象,而StreamAllocation使用Address創建RouteSelector對象,在連接時RouteSelector確定請求的路由。

每個Requst都會構造一個Address對象,構造好了Address對象只是有了與伺服器連接的配置信息,但沒有確定最終伺服器的ip,也沒有確定連接的路由。

2、創建RouteSelector
在StreamAllocation聲明的同時會聲明路由選擇器RouteSelector,為一次請求尋找路由。

3、選擇可用的路由Route

下面在測試過程跟蹤實例對象來理解,分別測試直連和HTTP代理HTTP2請求路由的選擇過程:
● 直連請求流程
● HTTP代理HTTPS流程
請求url: https://www.jianshu.com/p/63ba15d8877a

1、構造address對象

2、讀取代理配置:resetNextProxy

3、解析目標伺服器套接字地址:resetNextInetSocketAddress

4、選擇Route創建RealConnection

5、確定協議

測試方法:
● 在PC端打開Charles,設置埠,如何設置代理,網上有教程,比較簡單;
● 手機打開WIFI,選擇連接的WIFI修改網路,在高級選項中設置中指定了代理伺服器,ip為PC的ip,埠是Charles剛設置的埠;
● OkHttpClient不指定代理,發起請求。

1、構造address對象

2、讀取代理配置:resetNextProxy

3、解析目標伺服器套接字地址:resetNextInetSocketAddress

4、選擇Route創建RealConnection

5、創建隧道
由於是代理https請求,需要用到隧道代理。

從圖可以看出,建立隧道其實是發送CONNECT請求,header包括欄位Proxy-Connection,目標主機名,請求內容類似:

6、確定協議,SSL握手

1、代理可分為HTTP代理和SOCK代理;
2、HTTP代理又分為普通代理和隧道代理;普通代理適合明文傳輸,即http請求;隧道代理僅轉發TCP包,適合加密傳輸,即https/http2;
3、SOCK代理又分為SOCK4和SOCK5,區別是後者支持UDP傳輸,適合代理聊天工具如QQ;
4、沒有設置代理(OkHttpClient沒有指定同時系統也沒有設置),客戶端直接與目標伺服器建立TCP連接;
5、設置了代理,代理http請求時,客戶端與代理伺服器建立TCP連接,如果代理伺服器是域名,則解釋代理伺服器域名,而目標伺服器的域名由代理伺服器解析;
6、設置了代理,代理https/http2請求時,客戶端與代理伺服器建立TCP連接,發送CONNECT請求與代理伺服器建立隧道,並進行SSL握手,代理伺服器不解析數據,僅轉發TCP數據包。

如何正確使用 HTTP proxy
OkHttp3中的代理與路由
HTTP 代理原理及實現(一)

㈡ okhttp使用中自己要處理哪些錯誤和異常

OkHttp是一個相對成熟的解決方案,據說Android4.4的源碼中可以看到HttpURLConnection已經替換成OkHttp實現了。所以我們更有理由相信OkHttp的強大。
OkHttp 處理了很多網路疑難雜症:會從很多常用的連接問題中自動恢復。如果您的伺服器配置了多個IP地址,當第一個IP連接失敗的時候,OkHttp會自動嘗試下一個IP。OkHttp還處理了代理伺服器問題和SSL握手失敗問題。
使用 OkHttp 無需重寫您程序中的網路代碼。OkHttp實現了幾乎和java.net.HttpURLConnection一樣的API。如果你用了 Apache HttpClient,則OkHttp也提供了一個對應的okhttp-apache 模塊。

㈢ Android Okhttp/Retrofit網路請求加解密實現方案

比較安全的方案應該是AES+RSA的加密方式。具體如下圖所示。

為什麼要這樣做呢?
1、RSA是非對稱加密,公鑰和私鑰分開,且公鑰可以公開,很適合網路數據傳輸場景。但RSA加密比較慢,據說比AES慢100倍,且對加密的數據長度也有限制。
2、AES是對稱加密,加密速度快,安全性高,但密鑰的保存是個問題,在網路數據傳輸的場景就很容易由於密鑰泄露造成安全隱患
3、所以,AES+RSA結合才更好,AES加密數據,且密鑰隨機生成,RSA用對方(伺服器)的公鑰加密隨機生成的AES密鑰。傳輸時要把密文,加密的AES密鑰和自己的公鑰傳給對方(伺服器)。對方(伺服器)接到數據後,用自己的私鑰解密AES密鑰,再拿AES密鑰解密數據得到明文。這樣就綜合了兩種加密體系的優點。
4、除上面說的外,還可以加簽名,即對傳輸的數據(加密前)先做個哈希,然後用自己的RSA私鑰對哈希簽名(對方拿到自己的公鑰可以驗簽),這樣可以驗證傳輸內容有沒有被修改過。

就java來說,加密的輸入和輸出都是位元組數組類型的,也就是二進制數據,網路傳輸或本地保存都需要重新編碼為字元串。推薦使用Base64。Android 有自帶的Base64實現,flag要選Base64.NO_WRAP,不然末尾會有換行影響服務端解碼。
Android中Base64加密

總而言之,這些不同語言都有實現庫,調用即可,關鍵是參數要一致,具體還需要和後台聯調一下。
rsa加解密的內容超長的問題解決

現在說到網路框架,應該毫無疑問是Retrofit了。上面說的加密方案說到底還是要在網路請求框架內加上,怎麼做入侵最小,怎麼做最方便才是重點。
1、坑定不能直接在介面調用層做加密,加參數,這樣每個介面都要修改,這是不可能的。
2、ConverterFactory處理,這也是網上可以搜到的很多文章的寫法,但我覺得還是有入侵。而且有點麻煩。
3、OkHttp添加攔截器,這種方法入侵最小(可以說沒有),實現呢也非常優雅。
下面的實現,網上也找不到多少可以參考的文章,但不得不說,OkHttp的封裝和設計真的很好用,所見即所得。看下源碼,就知道該怎麼用了,連文檔都不用查。

主要注意點:
0、和介面無關的新加的數據放在請求頭里。
1、該close的要close,不然會內存泄漏。
2、新舊Request和Response要區分好,新的要替換舊的去傳遞或返回。
3、要對response.code()做處理,只有在和後台約定好的返回碼下才走解密的邏輯,具體看自己的需求,不一定都是200。

㈣ Android網路請求庫【OkHttp4.9.3】基本用法與原理分析

OkHttp是一套處理 HTTP 網路請求的依賴庫,由 Square 公司設計研發並開源,目前可以在 Java 和 Kotlin 中使用。對於 Android App 來說,OkHttp 現在幾乎已經占據了所有的網路請求操作,Retrofit + OkHttp實現網路請求似乎成了一種標配。因此它也是每一個 Android 開發工程師的必備技能,了解其內部實現原理可以更好地進行功能擴展、封裝以及優化。

OkHttp的高效性體現在:

第一步:創建OkHttpClient,創建OkHttpClient有兩種方式:

OkHttpClient提供了豐富的配置方法,例如添加攔截器、指定連接池、設置請求超時等等。

第二步:創建請求

使用Request.Builder() 構建Request實例

第三步:發起網路請求

OkHttp支持同步和非同步兩種請求方式

OkHttp的使用方法非常簡單,三步操作就可以發起一個簡單的同步或非同步請求。我們也可以很輕松地對網路請求進行配置,例如添加請求頭、設置請求方式、設置請求超時等等,這些配置參數會在源碼分析過程中詳細介紹。

現在我們已經學會了三步操作發起網路請求,接下來以這三個步驟為切入點,深入到源碼中學習OkHttp的實現原理,廢話少說馬上開車。

OkHttpClient創建方式有兩種,我們看看兩種方式有什麼區別。

第一種直接使用默認構造函數,內部依然是使用建造者模式

第二種使用建造者模式

兩種方式最終都是調用構造函數OkHttpClient(builder:Builder),由參數builder負責所有的參數配置工作。

當您創建單個OkHttpClient實例並將其用於所有 HTTP 調用時,OkHttp 性能最佳。 這是因為每個OkHttpClient都擁有自己的連接池和線程池,重用連接和線程可減少延遲並節省內存。 相反,為每個請求創建一個客戶端會浪費空閑池上的資源。

Request同樣使用建造者模式來創建,這里貼上部分重要源碼,很簡單就不細說了。

OkHttp發起網路請求分為同步請求和非同步請求兩種方式,我們只分析非同步請求流程,因為只要理解了非同步請求過程,基本上也就明白同步請求是怎麼一回事了。

RealCall是連接應用層與網路層的橋梁,負責處理連接、請求、響應和數據流。

Dispatcher維護著一套非同步任務執行策略,分析策略之前先介紹幾個重要概念:

client.dispatcher.enqueue(AsyncCall(responseCallback)) 執行步驟為:

AsyncCall實現了Runnable介面,因此一旦被線程池中的線程處理就會調用它的run()方法:

話休絮煩,我們開始分析攔截器責任鏈:

責任鏈執行流程:首先獲取當前攔截器interceptor,並且調用interceptor.intercept(next)執行攔截器操作。這里的next表示的是index+1後的責任鏈對象,攔截器的intercept()方法內部會調用next.proceed(request)方法再次進入到責任鏈,由於此時index已經加1,所以處理的是下一個攔截器。

如此循環往復,直到處理完責任鏈上最後一個攔截器為止。

注意除最後一個攔截器CallServerInterceptor不會調用chain.proceed(request)方法之外,其他攔截器都應該至少調用一次chain.proceed(request)方法。

為了驗證上面的結論,我們進入到RetryAndFollowUpInterceptor的intercept()方法一探究竟:

可以看到注釋1處重新進入責任鏈處理下一個攔截器。

有興趣可以自行查看最後一個攔截器CallServerInterceptor源碼,此處只給出本人閱讀源碼後得出的結論:

以上就是攔截器責任鏈的工作流程,我們再通過流程圖仔細感受一下。

分析完攔截器責任鏈,我們繼續分析AsyncCall#run()方法:

我們看到,如果()方法成功獲得服務端返回的數據,則調用responseCallback.onResponse(this@RealCall, response)方法完成非同步回調;如果服務端數據獲取失敗(請求異常),則調用responseCallback.onFailure(this@RealCall, canceledException)方法完成非同步回調

需要注意的是,responseCallback回調是在子線程中完成的,所以如果想把數據顯示到UI上,需要切換回主線程進行UI操作。

OkHttp發起網路請求全過程:

【知識點】OkHttp 原理 8 連問

閱讀全文

與okhttp源碼使用相關的資料

熱點內容
程序員需要會盲打么 瀏覽:446
編譯c無法識別unsighed 瀏覽:433
怎麼給幾年前的安卓機強行刷機 瀏覽:316
天方地圓製作演算法 瀏覽:193
演算法失效分析 瀏覽:760
gcc編譯選項給gdb調試 瀏覽:590
ios和android前景好 瀏覽:66
蘋果如何藍牙傳送安卓app 瀏覽:552
方舟編譯器mod怎麼用 瀏覽:762
伺服器地址欄在哪裡 瀏覽:397
做安檢還是程序員好 瀏覽:529
程序員最火的bug 瀏覽:938
騰訊文件夾英文怎麼寫 瀏覽:127
pdf內碼 瀏覽:434
微信小程序文件夾怎麼發給好友 瀏覽:972
java不能被繼承的類 瀏覽:163
蘋果app網址怎麼添加 瀏覽:910
php明年的今天 瀏覽:115
麒麟970也能用方舟編譯器么 瀏覽:476
金融實驗大作業python 瀏覽:796