OkHttp网络请求分析

http请求.gif

http请求如上所示:一个请求对应一个响应,一来一回。是一个上层协议,隐藏了许多细节。下面大概流程为:

  1. 地址解析 : 解析出协议,端口,主机IP
  2. 封装HTTP请求数据包
  3. 封装成TCP包,建立TCP连接(tcp三次握手)
  4. 客户机发送请求命令
  5. 服务器响应
  6. 服务器关闭TCP连接
    头信息加入了这行代码 Connection:keep-alive TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

注意:http协议中基于tcp/ip

OkHttp对以上步骤的实现解析

okhttp2.gif
  • RetryAndFollowUpInterceptor创建StreamAllocation对象,处理http的重定向及出错重试。对后续Interceptor的执行的影响为修改Request并创建StreamAllocation对象。

  • BridgeInterceptor补全缺失的一些http header。对后续Interceptor的执行的影响主要为修改了Request。Connection:keep-alive这里被默认添加

  • CacheInterceptor处理http缓存。对后续Interceptor的执行的影响为,若缓存中有所需请求的响应,则后续Interceptor不再执行。

  • ConnectInterceptor借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响为,创建了HttpStream和connection。

  • CallServerInterceptor作为Interceptor链中的最后一个Interceptor,用于处理IO,与服务器进行数据交换。
    https://www.jianshu.com/p/5c98999bc34f

参考:
OkHttp3中的代理与路由
OkHttp3 HTTP请求执行流程分析
Http工作原理

使用方法

//1.初始化OKHttp
OkHttpClient okHttpClient = new OkHttpClient()
                .newBuilder()
                .build();
//2.构建request
        final Request request = new Request.Builder()
                .get()
                .url(HttpUrl.parse("http://www.baidu.com/"))
                .build();
//3.发送请求(这里并没有执行,只是构建了`RealCall`对象)
        Call call = okHttpClient.newCall(request);
//4.接收响应(请求执行,并得到响应结果)
        Response execute = call.execute();
//5.输出
        System.out.println(execute.body().string());

分析

着重分析步骤3与4,步骤3是为了返回RealCall对象,步骤4则执行的是RealCall.execute()

RealCall.execute
@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
RealCall.getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

.okhttp 地址解析

//解析得到host  ---  BridgeInterceptor
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
//解析得到主机ip ---  StreamAllocation#findConnection
1. StreamAllocation#newStream()
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);

2.StreamAllocation#findHealthyConnection()
 RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          pingIntervalMillis, connectionRetryEnabled);

3.StreamAllocation#findConnection()
 if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
      newRouteSelection = true;
      routeSelection = routeSelector.next();
    }
4.RouteSelector#next()
public Selection next() throws IOException {
...
      Proxy proxy = nextProxy();
...
    return new Selection(routes);
  }

5.RouteSelector#nextProxy()
private Proxy nextProxy() throws IOException {
    if (!hasNextProxy()) {
      throw new SocketException("No route to " + address.url().host()
          + "; exhausted proxy configurations: " + proxies);
    }
    Proxy result = proxies.get(nextProxyIndex++);
    resetNextInetSocketAddress(result);
    return result;
  }

6.RouteSelector#resetNextInetSocketAddress()
socketHost = getHostString(proxySocketAddress);

与服务器tcp连接

ConnectInterceptor#intercept

HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);

findHealthyConnection -> findConnection -> RealConnection#connect -> connectSocket ->

private void connectSocket(int connectTimeout, int readTimeout, Call call,
      EventListener eventListener) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket()
        : new Socket(proxy);

    eventListener.connectStart(call, route.socketAddress(), proxy);
    rawSocket.setSoTimeout(readTimeout);
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
      ce.initCause(e);
      throw ce;
    }

设置okhttp的超时,这里就可以看得很明白了,其实是设置socket的连接超时与读写超时
.connectTimeout(30*1000, TimeUnit.MILLISECONDS) .readTimeout(30*1000,TimeUnit.MILLISECONDS) .writeTimeout(30*1000,TimeUnit.MILLISECONDS)

发送请求命令

CallServerInterceptor#intercept 这是okhttp中的最后一个入拦截器,主要作用是 与服务器进行数据交换

httpCodec.writeRequestHeaders(request);
Http1Codec#writeRequestHeaders

  public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }

这里的IO操作都是OKIO实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 简介 目前在HTTP协议请求库中,OKHttp应当是非常火的,使用也非常的简单。网上有很多文章写了关于OkHttp...
    第八区阅读 1,398评论 1 5
  • 一.网络通信概念理解 1.http协议概述 当我们在自己电脑的web浏览器地址栏敲入网址url,点击enter,...
    铜雀春深锁不住阅读 4,994评论 0 3
  • OkHttp解析系列 OkHttp解析(一)从用法看清原理OkHttp解析(二)网络连接OkHttp解析(三)关于...
    Hohohong阅读 21,022评论 4 58
  • 周六加班,此时的办公室里只有我一个人,虽然不提倡加班,但是负责的工作还是要按时完成,不要落下进度,突然间想起来昨天...
    3e81098bf2ef阅读 188评论 0 0
  • 女人,你为什么要做微商?你知道为什么吗? 栏目:行业动态2016-09-05 10:09:51 微商就是:“就算有...
    异葩阅读 553评论 0 0