关于TCP三次握手的理解:防止已失效的连接请求突然到达。我们来举个例子,为什么需要握手三次而不是两次。
我们来想象这样一个场景,小明去饭馆吃饭,去的时间比较晚已经过了饭点了。
同样是上面的场景,我们来稍微变化一下细节
这时候如果是两次握手,没有第三步,老板不管小明还在不在店里了在第二次握手时就会通知厨师等着做饭,厨师估计等个很久也没人点菜,这不浪费资源么,明明可以回家happy的,所以三次握手的关键就在于,它可以应变连接请求是否失效。
TCP协议需要断开连接时遵循四次挥手的原则,我们抛开专业术语而只是描述四次挥手的过程,我们仍旧拿三次握手中的点菜来举例。
三次握手后,小明开始点菜了,当点完菜后出现这种场景:
对于okhttp的同步请求,我们很清晰地知道是这样的用法:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
我们可以看到调用了execute()方法,我们来一一分析:
Step1
首先调用了OkHttpClient的newCall()方法来产生一个接口Call;
Step2
我们查看newCall的源码发现newCall实际上产生了一个RealCall,RealCall是Call的实现类,所以实际上同步请求调用了RealCall的execute()方法;
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false);
}
step3
通过源码我们发现client.dispatcher().executed(this);
这条语句,实际上RealCall的execute()方法最终执行了client.dispatcher()的executed()方法。client.dispatcher()是一个Dispatcher对象。
@Override
public Response execute() throws IOException {
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
step4
Dispatcher类是什么呢?我们通过它的名字”调度员”,和一系列的参数可以看出一些端倪,它应该就是一个用来分配各个请求如何进行工作的吧。
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
ExecutorService executorService;
Deque readyAsyncCalls = new ArrayDeque<>();
Deque runningAsyncCalls = new ArrayDeque<>();
Deque runningSyncCalls = new ArrayDeque<>();
对于同步请求来讲,它最终调用了Dispatcher类的executed()方法。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看到我们将call这个任务放入到了一个队列中去,这个队列中的任务会被外部某个地方调用来执行请求操作,当一个Call请求任务执行完之后,外部会调用Dispatcher类的finished()方法,finished()方法来看看还有没有请求需要被执行了。
OKhttp异步的网络请求和同步网络请求前三步基本一致,只是同步请求调用execute()方法,异步调用enqueue()方法,
Step1、Step2、Step3省略…
Step4
同步的时候我们不管如何,可以直接将任务添加到正在执行的队列中去,但是对于异步请求来讲,我们需要考虑最大并发数量,所以这时候需要用两个标准去判别是否到达可以立马执行的情况。OKhttp中用当前正在并发的请求不能超过64且同一个地址的访问不能超过5个来表示可以立即进入线程池执行。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
Step5
在这里我们分两种情况:
概述
okio是对Javaio的优化封装,解决一些Javaio的弊端,对于io来讲无非就是围绕着输入输出来讲的。在okio中,我们用sink、source分别代表写、读。当我们层层去查看sink、source的源码时,我们可以知道,它的简单结构是这样的。
我们进行的数据操作实际上来讲是在Buffer类上进行操作的,Buffer里面包含了什么呢?okio把数据拆成若干个segment数据,然后通过双向链表的方式将这些所有的数据连接起来,那么这样,Buffer只需要记住一个segment和它的上一个、下一个segment位置,Buffer就能获取所有的数据了。
对Segment而言,它才是真真正正存放数据的类,并且它是双向链表的结构,所以我们看一下segment的这些操作。
Segment
SegmentPool
是一个Segment组成的单链表,SegmentPool的作用就是管理多余的Segment,不直接丢弃废弃的Segment,等客户需要Segment的时候直接从池中获取一个对象,避免了重复创建新兑现,提高资源利用率。
BufferString
它实际上就是一个对String进行封装的类。
Okio的超时机制
同步超时机制、异步超时机制。
okio的优雅之处