OKHttp网络访问框架设计

OKHttp网络访问框架设计

一、OSI七层模型介绍


OSI(Open System Interconnection)意为开放式系统互联,把网络通信的工作分为7层,分别是物理层,数据链路层,网络层,传输层,会话层,表示层和应用层。国际标准组织(国际标准化组织)制定了OSI(Open System Interconnection)模型。 

二、TCP/IP模型 

TCP/IP不是一个协议,而是一个协议族的统称。里面包括了IP协议,IMCP协议,TCP协议,以及我们更加熟悉的http、ftp、pop3协议等等。计算机有了这些,就像是一个人掌握了世界上所有国家的语言,可以和任何国家的的不用语言的人类交流。计算机也一样,能够和任何计算机终端自由通信并传输数据了。


TCP: 负责应用软件(比如你的浏览器)和网络软件之间的通信。 

IP :负责计算机之间的通信。 

四、TCP的三次握手与四次挥手以及与UDP的区别 

1、TCP三次握手 

建立一次TCP通信,需要客户端和服务端发送3个包来确认连接建立成功; 

流程如下: 


(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给LISTEN状态下的Server,Client进入SYN_SENT状态,等待Server确认。 

(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。 

(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。 

–Syn Flood攻击–是DDOS攻击中的一种,简单暴力,通过大量请求消耗正常的带宽和协议栈处理资源的能力,从而达到服务端无法正常工作的目的。 

攻击原理:在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪 

2、TCP四次挥手 

断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开 

流程如下:


(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 

(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。 

(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。 

(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。 

–为什么建立连接是三次握手而断开连接需要四次挥手?– 

因为Server在LISTEN状态下,收到Client建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给Client。而关闭连接时,当收到Client的FIN报文时,仅仅表示Client不再发送数据了但是还能接收数据,Server也未必全部数据都发送给Client了,所以Server可以立即close,也可以发送一些数据给对方后,再发送FIN报文给Client来表示同意现在关闭连接,因此,Server一般都会分开发送ACK和FIN。 

3、TCP与UDP区别 

TCP 是面向连接的,UDP 是面向无连接的 

TCP程序结构复杂,UDP程序结构较简单 

TCP 是面向字节流的,UDP 是基于数据包的 

TCP 保证数据正确性,UDP 可能丢包 

TCP 保证数据顺序,UDP 不保证 

五、HTTP协议 

简介:HTTP是一种架构在TCP协议,应用在应用层的超文本传输协议 

工作原理:HTTP是基于客户/服务器模式,且面向连接的。 

(1)客户与服务器建立连接; 

(2)客户向服务器提出请求; 

(3)服务器接受请求,并根据请求返回相应的文件作为应答; 

(4)客户与服务器关闭连接。 

报文格式:有请求报文和响应报文 

请求报文格式:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体


图片资源来自:https://www.cnblogs.com/cr330326/p/9426018.html 

响应报文格式:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体


图片资源来自:https://www.cnblogs.com/cr330326/p/9426018.html 

六、OKHttp网络请求框架分析 

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() {// 需要自己创建子线程执行

@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)、创建OkHttpClient对象 

OkHttpClient okHttpClient = new OkHttpClient();//创建OkHttpClient对象

伪代码

publicclassOkHttpClientimplementsCloneable,Call.Factory,WebSocket.Factory{

publicOkHttpClient(){

this(newBuilder());

  }

  OkHttpClient(Builder builder) {

this.dispatcher = builder.dispatcher;// 调度员

    ......

  }

// 内部类

publicstaticfinalclassBuilder{

Dispatcher dispatcher;// 调度员

    ......

publicBuilder(){

dispatcher =newDispatcher();// 调度员

      ......

    }

  }

}

分析源码获知OkHttpClient对象的创建使用的是内部类Builder来完成的,这里使用的是构建者模式。 

(2)、创建Request对象 

final Request request = new Request.Builder()//创建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对象的创建也是以构建者模式创建的。 

(3)、创建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对象应该是RealCall,所以异步请求的执行应该是在RealCall中执行的 

(4)、异步请求执行

@Overridepublicvoidenqueue(Callback responseCallback){

synchronized(this) {// 检查call对象锁是否已经被别人持有

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

executed =true;

  }

  transmitter.callStart();

// 真正的执行交给了dispatcher调度员进行调度

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

}

call的执行中添加了同步锁,也就是说每一个call对象只能执行一次,如果被重复执行会提示”Already Executed”的IllegalStateException; 

由OkHttpClient的Builder我们知道调度员已经被创建过了,所以我们直接看Dispatcher#enqueue方法是怎么执行的

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

// 以下这些都是对调度员中的三种队列(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对象调用自己的executeOn方法让线程池去执行,executorService()方法的调用是为了创建线程池

    asyncCall.executeOn(executorService());

  }

returnisRunning;

}

//创建线程池

publicsynchronizedExecutorService executorService() {

if(executorService ==null) {

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

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

  }

returnexecutorService;

}

这里代码逻辑比较复杂,涉及到线程池和队列的使用,后面再详细分析。线程池 使用的是ThreadPoolExecutor实现的,三个队列分别是:

/** 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对象是怎么执行的呢? 

从源码获知最终放入线程池中的是AsyncCall 对象,AsyncCall 又继承了NamedRunnable,而NameRunnable又实现了Runnable接口,在线程中执行了execute方法,实际也就是去执行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 的实现

finalclassAsyncCallextendsNamedRunnable{

@Overrideprotectedvoidexecute(){

booleansignalledCallback =false;

      transmitter.timeoutEnter();

try{

Response response = getResponseWithInterceptorChain();// 获取响应结果,这里使用的是责任链模式

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{

// 失败,回调

responseCallback.onFailure(RealCall.this, e);

        }

}finally{

//最终完成任务,将call对象移除队列

client.dispatcher().finished(this);

      }

    }

  }

真正的网络请求和网络响应是通过getResponseWithInterceptorChain()方法处理的,这里不再详细分析,后面专门分析OKHttp框架使用到的责任链模式再详细分析。 

最终回调结果通过CallBack接口来处理

@Override

publicvoid onFailure(Callcall, IOException e) {

}

@Override

publicvoid onResponse(Callcall,Responseresponse) throws IOException {

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

}

4、OKHttp框架之线程池 

5、OKHttp框架之构建者模式 

6、OKHttp框架之责任链模式

你可能感兴趣的:(OKHttp网络访问框架设计)