一.TCP/IP协议家族
《图解http》,《http权威指南》,《Java网络编程基础》
Http(底层是基于socke), FTP, DNS,TCP,UDP,IP等等
OSI的七层协议:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
TCP三次握手(建立连接),和四次挥手(单开连接)
二.http请求
请求头
响应头
三.Cookie, Session,Token
3.1都是用来做持久化处理的,目的就是让客户端和服务器相互认实,Http请求默认是不持久的,没有状态的,谁也不认识谁。
3.2Cookie: 是存放在客户端的信息,这个信息是来自于服务器返回的信息,下次请求会带过去,如果用户离开网站后,Cookie已经过期(60s,和一个会话周期),会被清除,否则下次即使关闭了浏览器,再打开依然会被带到服务器。。
3.3Session:存放在服务器上面的客户端的信息,当会话周期(关闭浏览器之后)结束的时候会被清除(相对安全,比较耗资源)。
3.4Token(令牌): 再App里面用的最多,用来做用户身份的验证,有点类似于Cookie,相对来说更安全,一般流程:
3.4.1 客户端向服务端申请Token
3.4.2 服务端收到请求,会去验证请求,会签发一个Token给客户端,并且自己保存Token
3.4.3 客户端收到Token会保存起来,每次请求都会带上Token
3.4.4 服务器收到其他请求,会取验证客户端的Token,如果成功返回数据,不成功啥都没有。
四.http缓存
Cache-Control (缓存策略) : Public, Private,no-Cache,max-age(为0,要缓存,但是立刻过期),no-Store(不缓存)
Expires(缓存过期策略):指明了缓存数据有效的绝对时间,告诉客户端到了这个时间(比照客户端时间点)后本地缓存就作废了,在这个时间点内,客户端可以认为缓存数据是有效的,可之间从缓存中加载展示。
5.http状态码:
1xx:Informational(信息状态码) :接收的请求正在处理
2xx: 200(成功) Success : 请求处理完毕
3xx: Redirection(重定向) , 需要进行附加操作,一般是没有相应数据返回的。
比如304 (Not Modified) : 服务器没有发生过改变。
3.7(Internal Redirection) : 重定向
4xx(Connect error) : 资源找不到,是客户端错误状态码,服务器无法处理请求,可能是客户端输错了
5xx(Server error): 服务端错误, 服务器处理请求出错。 如500
五,http和https的区别:
Https = http + 加密 + 验证 +完成
端口: http(默认80端口) https(默认端口为443)
Http的缺点:1.数据没加密传输(所以可以抓它们的接口) ——没加密
2.不验证通信方的身份,可能会遭遇伪装 ——不验证通信方
3.无法验证报文的完整性,可能会遭遇篡改 —— 不验证报文完整性
TLS/SSL协议 :
1.加密(对称 和 非对称)
1.1 对称(AES , DES) 加密和解密用的是同一个密钥
1.2 非对称(RSA, DSA) 加密和解密用的不是同一个密钥
MD5 属于一种不可逆的,之加密不解密
2.证书(要钱,但便宜) ,建立连接的速度会拖慢,平时TCP会3次握手,加上证书之后,会进行8次握手。
六,http1.x 和 http2.x 比较
http2.0 采用二进制格式,而非文本格式。
http2.0 支持完全多路复用,
http2.0 使用报头压缩,节省开销
http2.0 让服务器主动将相应推送到客户端。(一种带内容推送)(不带内容推送)
七,异步和同步
1.异步和同步和线程没有关系,
举例:打电话
同步下的打电话:打电话——>处理(没挂断,你需要再电话边等)——>反馈
异步下的打电话:打电话——>处理(挂断,你不需要再电话边等)——>对方再打过来
八.整体框架和源码分析
7.1 自己写一个网络框架需要处理什么
1.网络是耗时的,需要开线程 ,new Thread不行所以要用到线程池
2.处理网络,HttpConnection 或者 要知道 输入输出流加+Socket
3.网络的请求头,信息处理(状态码) , 缓存处理, 文件格式上传的方式(表单提交,拼格式)
4.还需要知道一些路由的操作,http2.0 复用等
7.2Okhttp 大致内容: Okio(主要用于处理输入流), Socket
okio :(原生的JavaIO(应用了装饰设计模式,涉及到的类实在事太多了) + 自定义封装) 其实就是对于io的封装
socket链接:拦截器
7. 3 走一下大致流程
原版:(官方的介绍文档(基础)+ 自己阅读源码 + 项目实战验证)
第一部分:怎样用http发送一个Get请求的单流流程
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OkHttpClient okHttpClient = new OkHttpClient();
// 307 Location:https://www.baidu.com
// 1. 构建一个请求 ,url,端口,请求头的一些参数,表单提交(contentType,contentLength)
Request request = new Request.Builder()
.url("http://www.baidu.com").build();
// 2. 把 Request 封装转成一个 RealCall
Call call = okHttpClient.newCall(request);
// 3. enqueue 队列处理 执行
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 1-3 中小型企业
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
Log.e("TAG",result);
}
});
}
}
1. OkhttpCliet 执行异步请求的流程:
1.1 Call call = okHttpCliet.newCall(request);
@Override public Call newCall(Request request) {
// OkhttpCliet 实际返回的是一个RealCall
// 并将OkhttpCliet的上下文,以及请求头传到RealCall。
return RealCall.newRealCall(this, request, false /* for web socket */);
}
1.2 进入 RealCall.new RealCall(this, request, false); 内部看一下它做了什么。
//RealCall的 newRealCall 方法
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
//在构造方法当中将客户端发过来的客户端,请求头,Socket以及拦截器,并打包成一个真实的call
RealCall call = new RealCall(client, originalRequest, forWebSocket);
//将打包好的call 加入到时间监听器
call.eventListener = client.eventListenerFactory().create(call);
//并将call返回给 用户,供用户执行网络请求。
return call;
}
//RealCall的构造方法
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client; //初始OkhttpCliet
this.originalRequest = originalRequest; //初始化请求头
this.forWebSocket = forWebSocket; //初始化socket
//初始化拦截器
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
2. 执行call.enqueue(new Callback());
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 1-3 中小型企业
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
Log.e("TAG",result);
}
});
2.1 点进enqueue(new Callback); 发现它是一个抽象方法,并且接收一个用于回调的接口的实例。
void enqueue(Callback responseCallback);
2.2 发现RealCall 是 Call的实现类,而通过调用OkHttpCliet的newCall(request)方法是去实例化了一个接口Call的实例:
Call call = okHttpClient.newCall(request);
2.2 所以通过 call调用equeue(new Callback()) 这个方法的时候,实际上是调用Call的实现类RealCall复写父类的enqueue方法
@Override public void enqueue(Callback responseCallback) {
//用同步锁住当前方法,
synchronized (this) { //并不是不能进,只是一次锁内只能存在一个线程。
//如果当前的任务正在执行,就抛出异常,任务已经在执行
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//通过dispatcher()从线程池中调用线程去执行线程任务。
//AsyncCall(responseCallback) 是一个线程。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
2.3 然后我们查看这个 new AsyncCall(responseCallback),并发现 这个类是 RealCall 这个类的子类。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
//具体实现回调的地方
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
2.4 我们发现AsyncCall这个类继承自 NamedRunnable,我们再来查看这个类的源代码做了什么?
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
//更新线程的名字
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
//并在这里执行的execute这个方法
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
我们可以发现,这个类是一个抽象类,实现了Runnable接口,并定义了一个execute的抽象方法。并在复写自Runnable接口的run方法内执行了execute()这个方法,而这个方法的真正实现在子类AsyncCall 类中。
2.5看一下 通过dispatcher开启的线程池内部的配置
synchronized void enqueue(AsyncCall call) {
//判断当前正在执行的任务数量
//最大是64
//判断执行的任务中host(百度,阿里)的数量 最大是5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//如果正在运行的任务数量不大于64,或者任务中host不大于5 就将任务加导正在执行的集合中
runningAsyncCalls.add(call);
开启线程池
executorService().execute(call);
} else {
//如果任务数量大于64,或者任务中的host大于5 加入准备执行的集合,等待执行
readyAsyncCalls.add(call);
}
}
5.6 通过这个方法 executorService 去开启一个线程池 是一个CacheThreadPool
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
5.7 再来看一下 ExecutorService的 这个方法execute(call); 这个方法会调用 NamedRunnable 的run方法 ,run方法内执行了execute这个方法,这个方法在AsyncCall中实现了,具体的实现了回调的内容。
最后总结一下:
okhttpCliet 调用 newCall(request) 方法 去 new 一个RealCall 的实例返回, 由于RealCall是接口Call的实例类,所以调用Call的enqueue这个方法的时候,实际上调用的是RealCall的覆写自Call的方法enqueue,在enqueue这个方法当中通过 OkhttpCliet 的 dispatcher这个方法去开启线程池,并通过线程池的enqueue方法,向线程池中添加任务即AsyncCall ,AsyncCall这个类继承自NamedRunnable,顾名思义这是一个Runnable的实现类,并且是一个抽象类,实现了run方法,并在这个方法中执行了execute这个出响方法,这个方法的具体实现在AsyncCall中。