三、深入理解OkHttp:连接处理-ConnectIntercepter
终于来到OkHttp的网络连接模块,这块内容是OkHttp的核心内容。我们知道Http的连接需要进行3此握手,断开需要4次挥手。而连接的每一次握手,都需要进行Socket连接、释放,这是一个非常麻烦而且耗时耗力的过程。那么连接的服用就显得尤为重要了,同个地址的连接,如果在用完后不断开,保持连接,在下次的请求中便能重复使用这个连接,节省了连接的时间。 这对于大部分时间需要重复频繁访问同一个服务器地址的移动端网络来说更加不可或缺。
在本篇文章中,我们将以ConnectIntercepter为起点,跟随网络连接获取的过程,深入探究其中涉及到的:连接查找、连接复用,网络连接的建立(三次握手、Http2协议等的处理)。面对这复杂的过程,我们先总体的走一遍连接获取过程,然后在后续介绍 RealConnection.java 和 ConnectionPool.java 来更深入的理解连接的建立和缓存查找等逻辑。除此之外,我们还需要先看一下另一个类: Transmitter.java ,它将在connect的过程中起到重要的地位。
总结:Transmitter是在创建RealCall的时候被创建的,其中需要了OkHttpClient和当前请求Call作为参数。所以我们知道了,一个请求对应着一个Transmitter。而且,它的成员变量里有ExchangeFinder等类,负责为这个请求查找到一个合适的请求。
这个方法是释放一个连接,该方法在后面的查找连接中会涉及到,我们在这里先对其进行讲述。
总结 :这是一个请求关闭一个连接的过程。
从上面可以看到,在执行第一个默认拦截器的逻辑的时候,调用transmitter.prepareToConnect()方法。我们接下去看一下这个方法做了上面准备工作。
总结:其实这个方法,重点就是为连接作准备。但是主要目的还是找到可以复用的连接。它的逻辑如下:
总结: 这个方法是代表Transmitter获得了一个可用的连接了。那么它做的工作是将这个连接保存起来。然后将自己登记到RealConnection。这个方法后面会有用到,这里先讲解一下。
有了章节二的预备知识后,我们可以来看ConnectIntercepter了。不过他只是触发打开连接的按钮,真正连接的查找和连接逻辑在exchangeFinder.java和Exchage.java。不管怎么样,我们先来看一下开始的地方。
调用transmitter的newExcahge()方法,得到一个可以与远程地址进行通行的Exchage,然后就丢给下一个拦截器了。顺带说一下,在第一篇《》我们知道,紧跟着ConnectIntercepter的下一个拦截器是ServerIntercepter,那我们可以很容易的推理出,它拿到了ConnectIntercepter的excahge后,就进行了数据传输和数据接收。
调用exchangeFinder.find()找到一个连接,返回ExchangeCodec。ExchangeCodec是一个接口,它代表着Http请求的加密,和响应的解密。它有2个具体实现:Http1ExchangeCodec和Http2ExchangeCodec,它的详细内容详见【4】。我们继续看连接的查找。
总结: 该方法顾名思义,就是通过一个while(true)不断的找一个连接候选人,然后检查是否健康可用的,如果不能用就进行标记,丢弃。详细的如下:
接下来就是重中之重了,让我们来一起品味这很香的查找逻辑。
总结:这是一个查找连接的过程,在查找的时候,综合考虑了自身的连接,路由的结果,连接池的复用,和新建几种方案。具体的如下:
总结: 根据连接性质不一样,生成不同的数据加解密器。
章节小结:本节从ConnectIntercepter开始,追寻了一个连接如何被获得的过程,它涉及到了新建连接、路由选择,连接池复用等逻辑,最终的产物是Exchange,由它去到下一个拦截器:ServerIntercepter进行网络传输工作。其中Exchange、RealConnectionPool起到了很重要角色,我们将在下一小节中解析
RealConnection,描述的是一次与远程服务器的连接,所以它需要具备与远程地址进行建立连接,通行的能力。这些能里我们可以在后续它的成员变量和方法中看出来。照例,我们来看一下的构造函数和成员变量。
总结: 一些主要的成员变量已经如上列出注释。接下来从它最重要的方法connect()入手来理解它的作用。
总结:该方法是Connection处理连接逻辑的地方,主要包括一下几点:
总结: 创建隧道连接,就是在Http代理的代理上建立Https连接。主要的做了如下事情:
总结: 该方法是与远程服务器地址建立起Socket连接,并获得输入输出流。具体的如下:
在这一步connect过后,socket完成了3次握手建立TCP连接。
总结: 该方法根据请求协议,来确定建立的连接是否需要进一步协议处理。具体的如下:
总结: 在这个方法里,连接将进行SSL配置,三次握手,证书校验等工作。具体的如下:
总结: 可以看到,对SSLScoket配置,就是遍历connectionSpecs集合,然后挑出适合于这个sslScoket的配置,然后进行要用。具体的如下:
总结: 对这个socket设置tls版本和密码套件
总结: 这个方法在后续的解析中会涉及到,所以先放在这里讲了。主要是用来判断这个连接可不可以复用的。判断条件如注释。
在3.6的findConnetion过程中,我们看到了很多次连接池的身影,它对连接的复用也起着绝对重要的位置,如果不仔细的理解它的话,查找连接这块的逻辑就会少一大快。照例,从它的出生、成员变量和构造函数来初步认识它。
在Builder()里创建默认的连接池。
总结: 可以看出到,ConectionPool才用代理模式,实际逻辑交给RealConnection()。5个最大空闲连接,每个连接可保活5分钟。
总结: 可以看出,这个连接池是用来管理同个地址的连接的。它提供根据地址查找可用连接、清除连接等功能。接下来介绍一下它的几个重要方法。
总结: 遍历保存的连接,调用RealConnection.isEligible() 来判断这个连接是否符合条件。将这个请求的Transmitter登记到RealConnection。
总结: 将一个连接变为空闲连接。如果此时这个连接不可用的话,将连接从连接集合中移除,并返回true。如果还可以,通知清理任务执行,并返回false。
总结: 该方法是将一个连接放入连接池中,然后执行清理任务,不过它会被堵塞住,直到【5.4】方法触发。
总结: 这是一个清理连接的方法,它做的使其如下:
小篇结:本篇是介绍OkHttp的网络连接建立。开篇先介绍了Trasnmitter这一重要的类,随后从ConnectIntercepter入手,深入研究了连接Connection的获取逻辑。在获取的过程中,我们将到了连接缓存的处理。当获取不到缓存的时候,便会新建一个全新的网络连接,在这个过程中会进行Http的3次握手等过程。在最后2小节中,分别介绍了在整个过程中的中心类,被查找对象:RealConnection。和管理缓存Connection的ConnectionPool。最后以一张图来总结这一过程