http以及Okhttp的一次Get请求的源码分析

一.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缓存

  彻底弄懂 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中。

猜你喜欢

转载自blog.csdn.net/lyfxh1314/article/details/86508511
今日推荐