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框架之责任链模式