社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
本系列主要分析OKHttp源代码的框架和设计思想,因为OKHttp实现了HTTP协议,所以在做源代码分析之前,对于HTTP的基础知识做一个简单的回顾还是非常有必要的,以免陷入知其然不知其所以然的尴尬境地。
本文主要基于《图解HTTP》进行介绍,从某种意义上说,本文也算是一篇读书笔记吧,若是有些地方说得不是很清楚,建议大家参考原书。另外,《HTTP权威指南》也是业界经典,如果大家有时间,建议也一起研读一下。
为了理解HTTP,我们有必要事先了解一下TCP/IP协议族。其是互联网相关联的协议集合的总称,通常使用的网络就是在TCP/IP协议族的基础上运作的,而HTTP属于它内部的一个子集,除此之外,还包括大家所熟知的FTP,DNS,TCP,UDP,IP等等协议。
学习过《计算机网络》的我们都应该知道OSI的七层协议模型。
其核心思想就是把数据信息包装起来,即封装:发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去。
值得一提的是,层次化之后,设计也变得相对简单了。处于应用层上的应用可以只考虑分派给自己的任务,而不需要弄清对方在地球上哪个地方、对方的传输线路是怎样的、是否能确保传输送达等问题。
最后我们以一张图结束本段落的介绍。
应用 HTTP 协议时,必定是一端担任客户端角色,另一端担任服务器端角色。仅从一条通信线路来说,服务器端和客服端的角色是确定的。并且,肯定是先从客户端开始建立通信的,服务器端在没有接收到请求之前不会发送响应。
HTTP 协议使用 URI 定位互联网上的资源。正是因为 URI 的特定功能,在互联网上任意位置的资源都能访问到。
HTTP 是一种无状态协议。协议自身不对请求和响应之间的通信状态进行保存。也就是说在 HTTP 这个级别,协议对于发送过的请求或响应都不做持久化处理。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成如此简单的。
可是随着 Web 的不断发展,我们的很多业务都需要对通信状态进行保存。于是我们引入了 Cookie 技术。有了 Cookie 再用 HTTP 协议通信,就可以管理状态了。
Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
HTTP 协议的初始版本中,每进行一个 HTTP 通信都要断开一次 TCP 连接。比如使用浏览器浏览一个包含多张图片的 HTML 页面时,在发送请求访问 HTML 页面资源的同时,也会请求该 HTML 页面里包含的其他资源。因此,每次的请求都会造成无畏的 TCP 连接建立和断开,增加通信量的开销。
为了解决上述 TCP 连接的问题,HTTP/1.1 和部分 HTTP/1.0 想出了持久连接的方法。其特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。旨在建立一次 TCP 连接后进行多次请求和响应的交互。在 HTTP/1.1 中,所有的连接默认都是持久连接。
持久连接使得多数请求以管线化方式发送成为可能。以前发送请求后需等待并接收到响应,才能发送下一个请求。管线化技术出现后,不用等待亦可发送下一个请求。这样就能做到同时并行发送多个请求,而不需要一个接一个地等待响应了。
比如,当请求一个包含多张图片的 HTML 页面时,与挨个连接相比,用持久连接可以让请求更快结束。而管线化技术要比持久连接速度更快。请求数越多,时间差就越明显。
用于 HTTP 协议交互的信息被称为 HTTP 报文。请求端(客户端)的 HTTP 报文叫做请求报文;响应端(服务器端)的叫做响应报文。HTTP 报文本身是由多行(用 CR+LF 作换行符)数据构成的字符串文本。
HTTP 报文大致可分为报文首部和报文主体两部分。两者由最初出现的空行(CR+LF)来划分。通常,并不一定有报文主体。
请求报文的首部内容由以下数据组成:
请求报文的示例,如下:
响应报文的首部内容由以下数据组成:
响应报文的示例,如下:
在报文众多的字段当中,HTTP 首部字段包含的信息最为丰富。首部字段同时存在于请求和响应报文内,并涵盖 HTTP 报文相关的内容信息。
使用首部字段是为了给客服端和服务器端提供报文主体大小、所使用的语言、认证信息等内容。
根据实际用途被分为以下4中类型
Cookie的工作机制是用户识别及状态管理。Web网站为了管理用户的状态会通过Web浏览器,把一些数据临时写入用户的计算机内。接着当用户访问该Web网站时,可通过通信方式取回之前存放的Cookie。
Cookie相关的首部字段如下:
HTTP 首部字段是可以自行扩展的。所以在 Web 服务器和浏览器的应用上,会出现各种非标准的首部字段。
以下就是一些最为常用的首部字段:
HTTP 状态码的职责是当客户端向服务端发送请求时,描述返回的请求结果。
HTTP 状态码如 200 OK ,以 3 位数字和原因短语组成。数字中的第一位指定了响应类别,后两位无分类。
不少返回的响应状态码都是错误的,但是用户可能察觉不到这点。比如 Web 应用程序内部发生错误,状态码依然返回 200 OK。
如果把报文想象成因特网货运系统中的箱子,那么实体就是报文中实际的货物。
我们可以看到,上面示例右图中深红色框的内容就是报文的实体部分,而蓝色框的两部分内容分别就是实体首部和实体主体。而左图中粉红框内容就是报文主体。
通常,报文主体等于实体主体。只有当传输中进行编码操作时,实体主体的内容发生变化,才导致它和报文主体产生差异。
内容编码和传输编码很容易搞混,这里放在一起说一下
异同如下:
通过以上内容,我们了解到,传输编码只定义作用于分块传输编码中,那么我们就来介绍一下分块传输编码。
在HTTP通信过程中,请求的编码实体资源尚未全部传输完成之前,浏览器无法显示请求页面。在传输大容量数据时,通过数据分割成多块,能够让浏览器逐步显示页面。这种把实体主体分块的功能称为分块传输编码(Chunked Transfer Coding)。
当使用持久连接时,在服务器写主体之前,必须知道它的大小并在 Content-Length 首部中发送。如果服务器动态创建内容,就可能在发送之前无法知道主体的长度。
分块编码为这种困难提供了解决方案,只要允许服务器把主体分块发送,说明每块的大小就可以了。因为主体是动态创建的,服务器可以缓冲它的一部分,发送其大小和相应的块,然后在主体发送完之前重复这个过程。服务器可以用大小为 0 的块作为主体结束的信号,这样就可以继续保持连接,为下一个响应做准备。从而就不需要在发送之前知道整个报文的大小了。
分块传输编码的报文示例如下:
发送邮件时,我们可以在邮件中写入文字并添加多份附件,这是因为采用了MIME机制,它允许邮件处理文本、图片、视频等多个不同类型的数据。而在MIME扩展中会使用一种称为多部分对象集合(Multipart)的方法,来容纳多份不同类型的数据。
相应地,HTTP 协议中也采纳了多部分对象集合,发送的一份报文主体内可包含多种类型实体。
多部分对象集合包含的对象如下:
文件下载中断,如果继续下载,我们需要一种可恢复机制,即能从之前下载中断处恢复下载。要实现该功能需要指定下载的实体范围。像这样,指定范围发送的请求叫做范围请求。
针对范围请求,响应会返回状态码为206 Partial Content的响应报文。如果服务端无法响应范围请求,则会返回状态码200 OK和完整的实体内容。
网关是转发其他服务器通信数据的服务器,接收从客户端发送过来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关。网关能使通信线路上的服务器提供非HTTP协议服务。
隧道是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序。
隧道可按要求建立起一条与其他服务器的通信线路,届时使用SSL等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。
隧道本身不会去解析HTTP请求。也就是说,请求保持原样中转给之后的服务器。
代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
代理服务器的好处:
代理服务器的分类
根据以上内容,我们知道有一种代理为缓存代理,即当代理转发从服务器返回的响应时,代理服务器将会保存一份资源的副本。优点在于利用缓存可避免多次从源服务器转发资源。
因为涉及缓存的HTTP首部字段有好多,所以服务器的缓存策略,分析起来比较麻烦,易出错。彻底弄懂 Http 缓存机制 - 基于缓存策略三要素分解法 将缓存机制看做是HTTP缓存策略三个要素(缓存存储策略,缓存过期策略,缓存对比策略)相互作用后的集合。分析和设置HTTP报文缓存头时,只要能从中精准的分解出缓存三要素,就能非常准确的预判到缓存设置最终能达到的效果。大家可以阅读参考之,这里就不详细赘述了。
HTTP虽然如此优秀,但也有缺点,主要缺点如下:
为了解决上述问题,HTTPS应运而生。其提供了身份验证、信息加密和完整性校验的功能,可以解决HTTP存在的安全问题。
经常在Web的登录页面和购物结算界面等使用HTTPS通信。另外,也可以使用HTTPS通信来防止运营商劫持。
HTTPS并非是应用层的一种新协议。只是HTTP通信接口部分用SSL(Secure Socket Layer)和TLS(Transport Layer Security)协议代替而已。注:TLS是在SSL基础上制定的升级版协议。
通常,HTTP直接和TCP通信。当使用SSL时,则演变成先和SSL通信,再由SSL和TCP通信了。简言之,所谓HTTPS,其实就是身披SSL协议这层外壳的HTTP。
在采用SSL后,HTTP就拥有了HTTPS的加密、证书和完整性保护这些功能。
SSL是独立于HTTP的协议,所以不光是HTTP协议,其他运行在应用层的SMTP和Telnet等协议均可配合SSL协议使用。可以说SSL是当今世界上应用最为广泛的网络安全技术。
非对称密钥加密最大的一个问题,就是无法证明公钥本身就是货真价实的公钥。比如,正准备和某台服务器建立非对称密钥加密方式下的通信时,如何证明收到的公开密钥就是原本预想的那台服务器发行的公开密钥。或许在公开密钥传输途中,真正的公开密钥已经被攻击者替换掉了。
为了解决上述问题,可以使用由数字证书认证机构(CA,Certificate Authority)和其相关机关颁发的公开密钥证书。
证书的运作流程如下:
在这个过程注意几点:
我们来观察一下HTTPS单向验证的握手流程
主要分为四个步骤:
在以上流程中,应用层发送数据时会附加一种叫做MAC(Message Authentication Code)的报文摘要。MAC能够查知报文是否遭到篡改,从而保证报文的完整性。
当然,HTTPS当然也有缺点:
因此,如果是非敏感信息则使用HTTP通信,只有在包含个人信息等敏感数据时,才利用HTTPS加密通信。
对于客户端的认证,本文不做重点介绍,一语带过,想要了解的同学,可以参考相关书籍。
除了对于服务端的认证,在某些情景下,还需要对客户端进行认证。HTTP/1.1使用的认证方式如下所示
HTTP标准建立时,制定者主要想把HTTP当做传输HTML文档的协议。但是随着时代的发展,HTTP标准自身就出现了各种瓶颈和限制,不能满足所有需求。
HTTP功能上的不足可通过创建一套全新的协议来弥补。可是目前基于HTTP的Web浏览器的使用环境已遍布全球,因此无法完全抛弃HTTP。有一些新协议的规则是基于HTTP的,并在此基础上添加了新的功能。
发展到SPDY,其真正从协议级别消除HTTP所遭遇的瓶颈。
有好几家Web浏览器已经针对SPDY做出了相应的调整,但SPDY在Web网站端却进展不佳。其一是因为一个网站多个域名,不能多路复用;其二是很多Web网站存在的问题并非仅仅是由HTTP瓶颈所导致。
WebSocket是一套新协议及API,即Web浏览器与Web服务器之间全双工通信标准。
连接刚开始时还是HTTP协议,所以由客户端先发起连接。成功握手确立WebSocket连接之后,通信时不再使用HTTP的数据帧,而采用WebSocket独立的数据帧。WebSocket协议支持全双工通信,因此服务器端不必等待请求,可直接发送数据。
目标是改善用户在Web时的速度体验。可以说HTTP 2.0是SPDY的升级版(其实也是基于SPDY设计的)。
由于篇幅的限制,HTTP 2.0的详细内容不再详细介绍,有兴趣的朋友,可以参考相关书籍和资料。
《图解HTTP》
《HTTP权威指南》
一篇文章带你详解 HTTP 协议
彻底弄懂 Http 缓存机制 - 基于缓存策略三要素分解法
HTTPS 原理浅析及其在 Android 中的使用
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!