OKHttp網(wǎng)絡(luò)訪問框架設(shè)計

OKHttp網(wǎng)絡(luò)訪問框架設(shè)計

一脑漫、OSI七層模型介紹


OSI(Open System Interconnection)意為開放式系統(tǒng)互聯(lián)志鞍,把網(wǎng)絡(luò)通信的工作分為7層,分別是物理層,數(shù)據(jù)鏈路層,網(wǎng)絡(luò)層,傳輸層,會話層,表示層和應(yīng)用層部逮。國際標準組織(國際標準化組織)制定了OSI(Open System Interconnection)模型枷恕。?

二、TCP/IP模型?

TCP/IP不是一個協(xié)議味抖,而是一個協(xié)議族的統(tǒng)稱评甜。里面包括了IP協(xié)議,IMCP協(xié)議,TCP協(xié)議,以及我們更加熟悉的http憎兽、ftp氮墨、pop3協(xié)議等等。計算機有了這些,就像是一個人掌握了世界上所有國家的語言,可以和任何國家的的不用語言的人類交流。計算機也一樣旬薯,能夠和任何計算機終端自由通信并傳輸數(shù)據(jù)了。


TCP: 負責(zé)應(yīng)用軟件(比如你的瀏覽器)和網(wǎng)絡(luò)軟件之間的通信适秩。?

IP :負責(zé)計算機之間的通信绊序。?

四硕舆、TCP的三次握手與四次揮手以及與UDP的區(qū)別?

1、TCP三次握手?

建立一次TCP通信骤公,需要客戶端和服務(wù)端發(fā)送3個包來確認連接建立成功抚官;?

流程如下:?


(1)第一次握手:Client將標志位SYN置為1,隨機產(chǎn)生一個值seq=J阶捆,并將該數(shù)據(jù)包發(fā)送給LISTEN狀態(tài)下的Server凌节,Client進入SYN_SENT狀態(tài),等待Server確認洒试。?

(2)第二次握手:Server收到數(shù)據(jù)包后由標志位SYN=1知道Client請求建立連接倍奢,Server將標志位SYN和ACK都置為1,ack=J+1垒棋,隨機產(chǎn)生一個值seq=K卒煞,并將該數(shù)據(jù)包發(fā)送給Client以確認連接請求,Server進入SYN_RCVD狀態(tài)捕犬。?

(3)第三次握手:Client收到確認后跷坝,檢查ack是否為J+1,ACK是否為1碉碉,如果正確則將標志位ACK置為1,ack=K+1淮韭,并將該數(shù)據(jù)包發(fā)送給Server垢粮,Server檢查ack是否為K+1,ACK是否為1靠粪,如果正確則連接建立成功蜡吧,Client和Server進入ESTABLISHED狀態(tài),完成三次握手占键,隨后Client與Server之間可以開始傳輸數(shù)據(jù)了昔善。?

–Syn Flood攻擊–是DDOS攻擊中的一種,簡單暴力畔乙,通過大量請求消耗正常的帶寬和協(xié)議棧處理資源的能力君仆,從而達到服務(wù)端無法正常工作的目的。?

攻擊原理:在三次握手過程中牲距,Server發(fā)送SYN-ACK之后返咱,收到Client的ACK之前的TCP連接稱為半連接(half-open connect),此時Server處于SYN_RCVD狀態(tài)牍鞠,當收到ACK后咖摹,Server轉(zhuǎn)入ESTABLISHED狀態(tài)。SYN攻擊就是Client在短時間內(nèi)偽造大量不存在的IP地址难述,并向Server不斷地發(fā)送SYN包萤晴,Server回復(fù)確認包吐句,并等待Client的確認,由于源地址是不存在的店读,因此嗦枢,Server需要不斷重發(fā)直至超時,這些偽造的SYN包將產(chǎn)時間占用未連接隊列两入,導(dǎo)致正常的SYN請求因為隊列滿而被丟棄净宵,從而引起網(wǎng)絡(luò)堵塞甚至系統(tǒng)癱瘓?

2、TCP四次揮手?

斷開一個TCP連接時裹纳,需要客戶端和服務(wù)端總共發(fā)送4個包以確認連接的斷開?

流程如下:


(1)第一次揮手:Client發(fā)送一個FIN择葡,用來關(guān)閉Client到Server的數(shù)據(jù)傳送,Client進入FIN_WAIT_1狀態(tài)剃氧。?

(2)第二次揮手:Server收到FIN后敏储,發(fā)送一個ACK給Client,確認序號為收到序號+1(與SYN相同朋鞍,一個FIN占用一個序號)已添,Server進入CLOSE_WAIT狀態(tài)。?

(3)第三次揮手:Server發(fā)送一個FIN滥酥,用來關(guān)閉Server到Client的數(shù)據(jù)傳送更舞,Server進入LAST_ACK狀態(tài)。?

(4)第四次揮手:Client收到FIN后坎吻,Client進入TIME_WAIT狀態(tài)缆蝉,接著發(fā)送一個ACK給Server,確認序號為收到序號+1瘦真,Server進入CLOSED狀態(tài)刊头,完成四次揮手。?

–為什么建立連接是三次握手而斷開連接需要四次揮手诸尽?–?

因為Server在LISTEN狀態(tài)下原杂,收到Client建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發(fā)送給Client您机。而關(guān)閉連接時穿肄,當收到Client的FIN報文時,僅僅表示Client不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù)往产,Server也未必全部數(shù)據(jù)都發(fā)送給Client了被碗,所以Server可以立即close,也可以發(fā)送一些數(shù)據(jù)給對方后仿村,再發(fā)送FIN報文給Client來表示同意現(xiàn)在關(guān)閉連接锐朴,因此,Server一般都會分開發(fā)送ACK和FIN蔼囊。?

3焚志、TCP與UDP區(qū)別?

TCP 是面向連接的衣迷,UDP 是面向無連接的?

TCP程序結(jié)構(gòu)復(fù)雜,UDP程序結(jié)構(gòu)較簡單?

TCP 是面向字節(jié)流的酱酬,UDP 是基于數(shù)據(jù)包的?

TCP 保證數(shù)據(jù)正確性壶谒,UDP 可能丟包?

TCP 保證數(shù)據(jù)順序,UDP 不保證?

五膳沽、HTTP協(xié)議?

簡介:HTTP是一種架構(gòu)在TCP協(xié)議汗菜,應(yīng)用在應(yīng)用層的超文本傳輸協(xié)議?

工作原理:HTTP是基于客戶/服務(wù)器模式,且面向連接的挑社。?

(1)客戶與服務(wù)器建立連接陨界;?

(2)客戶向服務(wù)器提出請求;?

(3)服務(wù)器接受請求痛阻,并根據(jù)請求返回相應(yīng)的文件作為應(yīng)答菌瘪;?

(4)客戶與服務(wù)器關(guān)閉連接。?

報文格式:有請求報文和響應(yīng)報文?

請求報文格式:請求行 - 通用信息頭 - 請求頭 - 實體頭 - 報文主體


圖片資源來自:https://www.cnblogs.com/cr330326/p/9426018.html?

響應(yīng)報文格式:狀態(tài)行 - 通用信息頭 - 響應(yīng)頭 - 實體頭 - 報文主體


圖片資源來自:https://www.cnblogs.com/cr330326/p/9426018.html?

六阱当、OKHttp網(wǎng)絡(luò)請求框架分析?

1俏扩、OKHttp的使用(GET方法)?

implementation 'com.squareup.okhttp3:okhttp:3.14.2'

private void okhttpGetRequest() {

Stringurl ="https://wwww.baidu.com";

OkHttpClientokHttpClient =newOkHttpClient();

finalRequestrequest=newRequest.Builder()

.url(url)

.get()//默認就是GET請求

.build();

finalCallcall=okHttpClient.newCall(request);

/*call.enqueue(newCallback() {// 異步請求

@Override

publicvoidonFailure(Callcall,IOExceptione) {

? ? ? ? }

@Override

publicvoidonResponse(Callcall,Responseresponse)throwsIOException{

Stringresult=response.body().string();

? ? ? ? }

? ? });*/

newThread(newRunnable() {// 需要自己創(chuàng)建子線程執(zhí)行

@Override

publicvoidrun() {

//直接execute call

Responseresponse=null;

try{

response=call.execute();// 同步請求

Stringresult=response.body().string();

}catch(IOExceptione) {

e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

}).start();

}

2、OkHttp之源碼分析?

以GET請求為例

String url ="https://wwww.baidu.com";

OkHttpClient okHttpClient =newOkHttpClient();

finalRequest request =newRequest.Builder()

? ? ? ? .url(url)

.get()//默認就是GET請求

? ? ? ? .build();

finalCallcall= okHttpClient.newCall(request);

call.enqueue(newCallback() {// 異步請求

? ? @Override

publicvoidonFailure(Callcall, IOException e) {

? ? }

? ? @Override

publicvoidonResponse(Callcall, Response response)throwsIOException {

? ? ? ? String result = response.body().string();

? ? }

});

(1)弊添、創(chuàng)建OkHttpClient對象?

OkHttpClient okHttpClient = new OkHttpClient();//創(chuàng)建OkHttpClient對象

偽代碼

publicclassOkHttpClientimplementsCloneable,Call.Factory,WebSocket.Factory{

publicOkHttpClient(){

this(newBuilder());

? }

? OkHttpClient(Builder builder) {

this.dispatcher = builder.dispatcher;// 調(diào)度員

? ? ......

? }

// 內(nèi)部類

publicstaticfinalclassBuilder{

Dispatcher dispatcher;// 調(diào)度員

? ? ......

publicBuilder(){

dispatcher =newDispatcher();// 調(diào)度員

? ? ? ......

? ? }

? }

}

分析源碼獲知OkHttpClient對象的創(chuàng)建使用的是內(nèi)部類Builder來完成的录淡,這里使用的是構(gòu)建者模式。?

(2)油坝、創(chuàng)建Request對象?

final Request request = new Request.Builder()//創(chuàng)建Request對象

偽代碼

publicfinalclassRequest{

finalHttpUrl url;

? ? ......

? Request(Builder builder) {

this.url = builder.url;

? ? ......

? }

publicstaticclassBuilder{

@NullableHttpUrl url;

? ? String method;

? ......

publicBuilder(){

this.method ="GET";

this.headers =newHeaders.Builder();

? ? }

}

同樣Request對象的創(chuàng)建也是以構(gòu)建者模式創(chuàng)建的赁咙。?

(3)、創(chuàng)建Call對象?

final Call call = okHttpClient.newCall(request);//

/**

* Prepares the {@code request} to be executed at some point in the future.

*/

@OverridepublicCallnewCall(Request request) {

returnRealCall.newRealCall(this, request,false/* for web socket */);

}

staticRealCallnewRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {

// Safely publish the Call instance to the EventListener.

RealCall call =newRealCall(client, originalRequest, forWebSocket);

call.transmitter =newTransmitter(client, call);

returncall;

}

查看源碼獲知免钻,真正的call對象應(yīng)該是RealCall,所以異步請求的執(zhí)行應(yīng)該是在RealCall中執(zhí)行的?

(4)崔拥、異步請求執(zhí)行

@Overridepublicvoidenqueue(Callback responseCallback){

synchronized(this) {// 檢查call對象鎖是否已經(jīng)被別人持有

if(executed)thrownewIllegalStateException("Already Executed");

executed =true;

? }

? transmitter.callStart();

// 真正的執(zhí)行交給了dispatcher調(diào)度員進行調(diào)度

client.dispatcher().enqueue(newAsyncCall(responseCallback));

}

call的執(zhí)行中添加了同步鎖极舔,也就是說每一個call對象只能執(zhí)行一次,如果被重復(fù)執(zhí)行會提示”Already Executed”的IllegalStateException链瓦;?

由OkHttpClient的Builder我們知道調(diào)度員已經(jīng)被創(chuàng)建過了拆魏,所以我們直接看Dispatcher#enqueue方法是怎么執(zhí)行的

voidenqueue(AsyncCall call) {

synchronized(this) {

readyAsyncCalls.add(call);// 將call對象放入準備隊列中

// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to

// the same host.

if(!call.get().forWebSocket) {

? ? ? AsyncCall existingCall = findExistingCallWithHost(call.host());

if(existingCall !=null) call.reuseCallsPerHostFrom(existingCall);

? ? }

? }

? promoteAndExecute();

}

privatebooleanpromoteAndExecute() {

assert(!Thread.holdsLock(this))

// 以下這些都是對調(diào)度員中的三種隊列(readyAsyncCalls、runningAsyncCalls慈俯、runningSyncCalls)的處理邏輯 start

List executableCalls =newArrayList<>();

booleanisRunning;

synchronized(this) {

for(Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {

? ? ? AsyncCall asyncCall = i.next();

if(runningAsyncCalls.size() >= maxRequests)break;// Max capacity.

if(asyncCall.callsPerHost().get() >= maxRequestsPerHost)continue;// Host max capacity.

? ? ? i.remove();

? ? ? asyncCall.callsPerHost().incrementAndGet();

executableCalls.add(asyncCall);

runningAsyncCalls.add(asyncCall);

? ? }

isRunning = runningCallsCount() >0;

? }

// 隊列處理end

for(inti =0,size= executableCalls.size(); i

AsyncCall asyncCall = executableCalls.get(i);

// call對象調(diào)用自己的executeOn方法讓線程池去執(zhí)行渤刃,executorService()方法的調(diào)用是為了創(chuàng)建線程池

? ? asyncCall.executeOn(executorService());

? }

returnisRunning;

}

//創(chuàng)建線程池

publicsynchronizedExecutorService executorService() {

if(executorService ==null) {

executorService =newThreadPoolExecutor(0, Integer.MAX_VALUE,60, TimeUnit.SECONDS,

newSynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher",false));

? }

returnexecutorService;

}

這里代碼邏輯比較復(fù)雜,涉及到線程池和隊列的使用贴膘,后面再詳細分析卖子。線程池 使用的是ThreadPoolExecutor實現(xiàn)的,三個隊列分別是:

/** Ready async calls in the order they'll be run. */

privatefinalDeque readyAsyncCalls =newArrayDeque<>();// 準備中的異步隊列

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */

privatefinalDeque runningAsyncCalls =newArrayDeque<>();// 運行中的異步隊列

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */

privatefinalDeque runningSyncCalls =newArrayDeque<>();// 運行中的同步隊列

那么線程池中的call對象是怎么執(zhí)行的呢刑峡??

從源碼獲知最終放入線程池中的是AsyncCall 對象洋闽,AsyncCall 又繼承了NamedRunnable玄柠,而NameRunnable又實現(xiàn)了Runnable接口,在線程中執(zhí)行了execute方法诫舅,實際也就是去執(zhí)行AsyncCall的execute()方法

publicabstractclassNamedRunnableimplementsRunnable{

protectedfinalString name;

publicNamedRunnable(String format, Object... args){

this.name = Util.format(format, args);

? }

@Overridepublicfinalvoidrun(){

? ? String oldName = Thread.currentThread().getName();

? ? Thread.currentThread().setName(name);

try{

? ? ? execute();

}finally{

? ? ? Thread.currentThread().setName(oldName);

? ? }

? }

protectedabstractvoidexecute();

}

// AsyncCall 的實現(xiàn)

finalclassAsyncCallextendsNamedRunnable{

@Overrideprotectedvoidexecute(){

booleansignalledCallback =false;

? ? ? transmitter.timeoutEnter();

try{

Response response = getResponseWithInterceptorChain();// 獲取響應(yīng)結(jié)果羽利,這里使用的是責(zé)任鏈模式

signalledCallback =true;

// 得到結(jié)果,回調(diào)

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{

// 失敗刊懈,回調(diào)

responseCallback.onFailure(RealCall.this, e);

? ? ? ? }

}finally{

//最終完成任務(wù)这弧,將call對象移除隊列

client.dispatcher().finished(this);

? ? ? }

? ? }

? }

真正的網(wǎng)絡(luò)請求和網(wǎng)絡(luò)響應(yīng)是通過getResponseWithInterceptorChain()方法處理的,這里不再詳細分析虚汛,后面專門分析OKHttp框架使用到的責(zé)任鏈模式再詳細分析匾浪。?

最終回調(diào)結(jié)果通過CallBack接口來處理

@Override

publicvoid onFailure(Callcall, IOException e) {

}

@Override

publicvoid onResponse(Callcall,Responseresponse) throws IOException {

Stringresult =response.body().string();

}

4、OKHttp框架之線程池?

5泽疆、OKHttp框架之構(gòu)建者模式?

6户矢、OKHttp框架之責(zé)任鏈模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市殉疼,隨后出現(xiàn)的幾起案子梯浪,更是在濱河造成了極大的恐慌,老刑警劉巖瓢娜,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挂洛,死亡現(xiàn)場離奇詭異,居然都是意外死亡眠砾,警方通過查閱死者的電腦和手機虏劲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褒颈,“玉大人柒巫,你說我怎么就攤上這事」韧瑁” “怎么了堡掏?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刨疼。 經(jīng)常有香客問我泉唁,道長,這世上最難降的妖魔是什么揩慕? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任亭畜,我火速辦了婚禮,結(jié)果婚禮上迎卤,老公的妹妹穿的比我還像新娘拴鸵。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布宝踪。 她就那樣靜靜地躺著侨糟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘩燥。 梳的紋絲不亂的頭發(fā)上秕重,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音厉膀,去河邊找鬼溶耘。 笑死,一個胖子當著我的面吹牛服鹅,可吹牛的內(nèi)容都是我干的凳兵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼企软,長吁一口氣:“原來是場噩夢啊……” “哼庐扫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仗哨,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤形庭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后厌漂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萨醒,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年苇倡,在試婚紗的時候發(fā)現(xiàn)自己被綠了富纸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡旨椒,死狀恐怖晓褪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情综慎,我是刑警寧澤辞州,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站寥粹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏埃元。R本人自食惡果不足惜涝涤,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岛杀。 院中可真熱鬧阔拳,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至货裹,卻和暖如春嗤形,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弧圆。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工赋兵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搔预。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓霹期,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拯田。 傳聞我的和親對象是個殘疾皇子历造,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345