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é)任鏈模式