你真的掌握了HTTP吗?

571 阅读43分钟

一、前言

这篇博客是继学习okhttp源码后,发现对http的一些基础知识点掌握的比较薄弱,所以补习了一下这方面的基础知识。本文主要讲http的具体的工作机制,各个版本之间的差异-包括http1.0、http1.1、http2.0(SPDY)和http3.0(QUIC)。还讲解了Http和Https之间的关系。

二、HTTP的前世今生

2.1 概念

这里还是谈谈HTTP是什么吧,如下:

  • 超文本传输协议(HTTP)是一种 通信协议,它允许将超文本标记语言(HTML)文档充web服务器传送到客服端的浏览器。
  • HTTP是一个属于应用层的面向对象的协议,由于其简捷快速的方式,适用于分布式流媒体信息系统。它于1990年提出,经过几年的使用ui发展,得到不断的完善和扩展.

2.2 诞生于发展

2.2.1 萌芽

1989年CERN(欧洲粒子物理研究所)中由Tim Berners-Lee(万维网之父) 领导的小组提交了一个针对Internet的新协议和一个使用该协议的文档系统该小组将这个新系统命名为Word Wide Web,它的目的在于使全球的科学家能够利用Internet交流自己的工作文档。这个新系统被设计为允许Internet上任意一个用户都可以从许多文档服务计算机的数据库中搜索和获取文档。1990年末,这个新系统的基本框架已经在CERN中的一台计算机中开发出来并实现了,这个也促使了http的诞生。

2.2.2 HTTP/0.9

1991Tim在之前的研究基础上写了一篇关于HTTP的文章,标志着HTTP的第一个版本诞生,这个版本中它的内容非常地简单。

  • 首先它只有一个命令GET。对应到现在的GET请求和POST请求,这些叫做HTTP的命令或者方法。
  • 它没有HEADER等描述数据的信息。因为这个时候的请求非常简单,它需要达到的目的也非常简单,没有那么多数据格式。
  • 规定采用TCP/IP请求,服务器发送完内容之后,就关闭TCP连接。

2.2.3 HTTP 1.0

1996年这个版本和现在普遍使用的HTTP/1.1差不多,在HTTP/0.9版本基础上进行了改进。

  • 增加了很多命令。比如:POST、PUT、HEADER这些命令。
  • 增加了status code和header相关的内容。status code是用来描述服务器端处理某一个请求之后的状态的;header主要包含:请求和发送数据的描述以及对这部分数据进行操作的方法。
  • 增加了多字符集支持、多部分发送、权限、缓存等相关的内容。这些内容有利于更好地使用http请求去实现WEB服务。

2.2.4 HTTP 1.1

1997年HTTP/1.1发布,这个版本主要是在HTTP/1.0的基础上增加了一些功能来优化网络连接的过程。

  • 在这个版本支持了持久连接。在HTTP/1.0版本里面,一个http请求要发送就要先在客户端和服务器端之间创建一个TCP连接,创建完这个TCP连接之后,等服务器端返回完数据之后,这个TCP连接就关闭了。这个成本是相对比较高的,因为在建立一个TCP连接的过程中要进行http的三次握手,这一部分是通过TCP来完成的,在创建这个连接的过程中消耗是比较高的,延迟也会比较高。所以如果在建立完一个连接之后,它可以不关闭,之后新的http请求就可以一直在这个连接里面进行数据发送的话,它的性能和效率肯定会提升很多,HTTP/1.1已经实现了这个功能。

  • 增加了pipeline。可以在同一个TCP连接里面发送多个http请求,就是上面说的那样。但是在HTTP/1.1里面,虽然是可以在同一个TCP连接里面发送多个http请求,但是服务器端对于进来的请求,是要按照顺序进行数据返回的。因此,如果前一个请求等待时间非常长,而后一个请求处理得比较快。这个时候后一个请求不能先发送,而是要等第一个请求数据全部发送完成之后,才能进行发送,即是串行的。等待的这部分时间就体现出了与并行传输性能之间的差距。而这个在HTTP/2里面得到了优化。

  • 增加了HTTP的头host和其他一些命令。其中比较重要的就是host,有了host之后就可以在同一台服务器(物理服务器)上同时跑多个web服务。比如说一个Node.js的web服务,一个Java的web服务。通过host这个字段来表示两个服务都是请求到同一个物理服务器上,但是我要请求的是里面哪一个软件服务,Node.js还是Java?这就是通过host来进行判断的。这个host头增加的好处就是,在同一个物理服务器或者同一个集群里面可以部署很多不同的web服务来,提高了物理服务器的使用效率。

2.2.5 HTTP 2.0/SPDY

2015年HTTP/2发布,这个协议主要基于SPDY协议,在不改动HTTP语义方法状态码URI首部字段的情况下,大幅度提高了web性能。

  • SPDY是Speedy的昵音,意为“更快”。它是Google开发的基于TCP协议应用层协议。目标是优化HTTP协议的性能,通过压缩多路复用优先级等技术,缩短网页的加载时间并提高安全性。SPDY协议的核心思想是尽量减少TCP连接数。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。
  • HTTP2.0中所有加强性能的核心是二进制传输,在HTTP1.x中,我们是通过文本的方式传输数据。基于文本的方式传输数据存在很多缺陷,文本的表现形式有多样性,因此要做到健壮性考虑的场景必然有很多,但是二进制则不同,只有0和1的组合,因此选择了二进制传输,实现方便且健壮。在HTTP2.0中引入了新的编码机制,所有传输的数据都会被分割,并采用二进制格式编码。
    http2.0.png
    在这里为了保证HTTP不受影响,那就需要在应用层(HTTP2.0)和传输层(TCP or UDP)之间增加一个二进制分帧层。在二进制分帧层上,HTTP2.0会将所有传输的信息分为更小的消息和帧,并采用二进制格式编码,其中HTTP1.x的首部信息会被封装到Headers帧,而Request Body则封装到Data帧
  • 多路复用HTTP 2.0协议将一个TCP的连接中,切分成多个流,每个流都有自己的ID,而且流可以是客户端发往服务端,也可以是服务端发往客户端。它其实只是一个虚拟的通道,流是有优先级的。客户端将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,而且根据优先级,决定优先处理哪个流的数据。
  • 头部压缩 在HTTP1.0中,我们使用文本的形式传输header,在header中携带cookie的话,每次都需要重复传输几百到几千的字节,这着实是一笔不小的开销。在HTTP2.0中,我们使用了HPACK (HTTP2头部压缩算法)压缩格式对传输的header进行编码,减少了header的大小。并在两端维护了索引表,用于记录出现过的header,后面在传输过程中就可以传输已经记录过的header的键名,对端收到数据后就可以通过键名找到对应的值。
  • 服务器推流 服务端有了推送功能,将客户端感兴趣的东西推给客户端,当客户端请求这些时,直接去缓存中取就行。
  • 优先权的依赖 每个流都有自己的优先级别,会表明哪个流是最重要的,客户端会指定哪个流是最重要的,有一些依赖参数,这样一个流可以依赖另外一个流。优先级别可以在运行时动态改变,当用户滚动页面时,可以告诉浏览器哪个图像是最重要的,你也可以在一组流中进行优先筛选,能够突然抓住重点流。

2.2.6 HTTP 3.0/QUIC

2016年IETFQUIC的基础上制定了HTTP 3.0的规则。QUIC(Quick UDP Internet Connections)基于UDP的传输层协议,提供像TCP一样的可靠性。在提高web应用性能上,可以选择在应用层使用HTTP2.0实现多路传输,在物理层使用CDN解决网络拥塞和最后一公里问题。在传输层,目前主要使用TCP,但由于TCP本身的问题(一个充满补丁的丑陋的协议),成为了限制web应用性能的一个瓶颈。具体的提升细节后面章节介绍。

三 好兄弟DNS

HTTP的底层是基于TCP/IP的,实际的交互是通过IP地址来进行访问。但是相对于IP地址(一组纯数字),域名更容易让人记住。所以必须有个机制或服务吧域名转换成IP地址,DNS服务就是用来解决这个问题的,他提供域名到IP地址之间的解析服务。

3.1 DNS的树状结构

每个人上网,都需要访问它,但是同时,这对它来讲也是非常大的挑战。一旦它出了故障,整个互联网都将瘫痪。另外,上网的人分布在全世界各地,如果大家都去同一个地方访问某一台服务器,时延将会非常大。因而,DNS服务器,一定要设置成高可用高并发分布式的。于是,就有了这样树状的层次结构:

DNS.png

  • 根DNS服务器 :返回顶级域DNS服务器的IP地址
  • 顶级域DNS服务器:返回权威DNS服务器的IP地址
  • 权威DNS服务器 :返回相应主机的IP地址

3.2 DNS流程

为了提高DNS的解析性能,很多网络都会就近部署DNS缓存服务器。于是,就有了以下的DNS解析流程。

  • 1. 电脑客户端会发出一个DNS请求,问 "www.163.com" 的IP是啥啊?。并发给本地域名服务器(本地DNS)。那本地域名服务器(本地DNS)是什么呢?如果是通过DHCP配置,本地DNS由你的网络服务商(ISP),如电信、移动等自动分配,它通常就在你网络服务商的某个机房。

  • 2. 本地DNS收到来自客户端的请求。你可以想象这台服务器上缓存了一张域名与之对应IP地址的大表格。如果能找到 ‘www.163.com’ ,它直接就返回IP地址。如果没有,本地DNS会去问它的根域名服务器:“老大,能告诉我 ‘www.163.com’ 的IP地址吗?”根域名服务器是最高层次的,全球共有13套。它不直接用于域名解析,但能指明一条道路。

  • 3. 根DNS收到来自本地DNS的请求,发现后缀是.com,说:“哦,‘www.163.com’ 啊,这个域名是由 ‘.com’ 区域管理,我给你它的顶级域名服务器的地址,你去问问它吧。”

  • 4. 本地DNS转向问顶级域名服务器:“老二,你能告诉我 ‘www.163.com’ 的IP地址吗?”顶级域名服务器就是大名鼎鼎的比如 ‘.com、.net、.org’这些一级域名,它负责管理二级域名,比如 ‘163.com’ ,所以它能提供一条更清晰的方向。

  • 5. 顶级域名服务器说:“我给你负责 ‘www.163.com’ 区域的权威DNS服务器的地址,你去问它应该能问到。”

  • 6. 本地DNS转向问权威DNS服务器:“您好,‘www.163.com’ 对应的IP是啥呀?” ‘163.com’ 的权威DNS服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。

  • 7. 权限DNS服务器查询后将对应的IP地址X.X.X.X告诉本地DNS。

  • 8. 本地DNS再将IP地址返回客户端,客户端和目标建立连接。

DNS 1.png

3.3 负载均衡

站在客户端角度,上面就是是一次DNS递归查询过程。因为本地DNS全权为它效劳,它只要坐等结果即可。在这个过程中,DNS除了可以通过名称映射为IP地址,它还可以做另外一件事,就是负载均衡,他可以根据不同的情况返回相应最优的IP地址。

3.3.1内部负载均衡

例如,一个应用要访问数据库,在这个应用里面应该配置这个数据库的IP地址,还是应该配置这个数据库的域名呢?显然应该配置域名,因为一旦这个数据库,因为某种原因,换到了另外一台机器上,而如果有多个应用都配置了这台数据库的话,一换IP地址,就需要将这些应用全部修改一遍。但是如果配置了域名,则只要在DNS服务器里,将域名映射为新的IP地址,这个工作就完成了,大大简化了运维。在这个基础上,我们可以再进一步。例如,某个应用要访问另外一个应用,如果配置另外一个应用的IP地址,那么这个访问就是一对一的。但是当被访问的应用撑不住的时候,我们其实可以部署多个。但是,访问它的应用,如何在多个之间进行负载均衡?只要配置成为域名就可以了。在域名解析的时候,我们只要配置策略,这次返回第一个IP,下次返回第二个IP,就可以实现负载均衡了。

3.3.2全局负载均衡

为了保证我们的应用高可用,往往会部署在多个机房,每个地方都会有自己的IP地址。当用户访问某个域名的时候,这个IP地址可以轮询访问多个数据中心。如果一个数据中心因为某种原因挂了,只要在DNS服务器里面,将这个数据中心对应的IP地址删除,就可以实现一定的高可用。另外,我们肯定希望北京的用户访问北京的数据中心,上海的用户访问上海的数据中心,这样,客户体验就会非常好,访问速度就会超快。这就是全局负载均衡的概念。

对于具体的负载均衡的知识这里不做过多的介绍,里面的水还是挺深的,后面有时间了再淌。

四 庖丁解牛HTTP

HTTP支持客户/服务器模式,工作方式是客户端向服务器发出请求服务器端响应请求,并进行相应服务,具体的流程如下。

image.png

4.1 HTTP的特点

  • 支持C/S模型,客户端向服务器请求时,只需要传送请求方法和路径。
  • 请求方法常用的有GET、POSH,每种方法规定了客户和服务器联系的类型不同。
  • HTTP允许传输任意类型的数据对象,正在传输的类型由Content-Type(Content-Type是HTTP包中用来表示内容类型的表示)加以标记。
  • 无连接其含义是限制每次连接值处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。
  • 无状态,具体是指协议对于失误处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则他必须重传,这样可能导致每次连接传送的数据量增大。另外在服务器不需要先前信息是他的应答就较快。

4.2 URI、URL和URN

URI、URL和URN的关系可以如下图表示:

image.png

  • URI:一个紧凑的字符串用来标示抽象或物理资源,一个URI可以进一步分为定位符、名字或两者都是。术语"Uniform Resource Locator"(URL)是URI的子集,除了确定一个资源,还提供一种定位该资源的主要访问机制(如其网络位置)。
  • URN作用就好像一个人的名字,URL就像一个人的地址。URN确定了东西的身份,URL提供了找到他的方式。
  • URL是URI的一种,但不是所有的URI都是URL。URI和URL最大的差别是"访问机制",URN是唯一表示的一部分,是身份信息。

4.3 HTTP报文结构

http结构.png
上图就是HTTP的请求报文结构。

http响应.png
上图就是HTTP的响应报文结构。

4.3.1 报文头

HTTP的报文头大体可以分为四类,分别是:通用报文头请求报文头响应报文头实体报文头。在HTTP 1.1里一共规范了47中报文头字段。

  • 通用报文头

通用报文头.png

  • 请求报文头

请求报文头.png

  • 响应报文头

响应报文头.png

  • 实体报文头

实体报文头.png

常见的报文字段:

ACCEPT:作用:浏览器端可以接受的媒体类型。

  • a、例如Accept: text/html代表浏览器可以接受服务器回发的类型为text/html也就是我们常说的html文档,如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(Non Acceptable)。
  • b、又例如Accept:*/*代表浏览器可以处理所有类型。
  • c、 如果想要给显示的媒体类型增加优先级,则使用q= 来额外表示权重值);重值q的范围是0~1 (可精确到小数点后3位) ,且1为最大值。不指定权重q值时,默认权重为q-1.0,当服务器提供多种内容时,将会首先返回权重值最高的媒体类型。

Accept-Language: 作用浏览器申明自己接收的语言。

  • 例如:Accept-Language: zh-cn,zh;q=0.7,en-us,en;q=0.3客户端在服务器有中文版资源的情况下,会请求其返回中文版对应的响应,没有中文版时,则请求返回英文版响应。

Connection: 作用表示是否保留上一次请求的TCP连接。

  • a、 例如Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
  • b、 例如Connection: close代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭,当客户端再次发送Request,需要重新建立TCP连接。

Host:作用请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的。

  • 例如在浏览器输入: www.fljf.com:8080 浏览器发送的请求消息中,就会包含Host请求报头域,如下: Host: www.fljf.com:8080 。

Referer: 当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。

User-Agnet: 作用告诉HTTP服务器,客户端使用的操作系统和浏览器的名称和版本。很多情况下我们会通过User-Agnet来判断浏览器类型,从而进行不同的兼容设计。

Content-Type: 作用:说明了报文体内对象的媒体类型

  • text/html: HTML格式
  • text/plain: 纯文本格式
  • text/xml: XML格式
  • image/gif: gif图片格式
  • image/jpeg: jpg图片格式
  • mage/png: png图片格式
  • application/xhtml+xml: XHTML格式
  • application/xml: XML数据格式
  • application/atom+xml: Atom XML聚合格式
  • application/json: JSON数据格式
  • application/pdf: pdf格式
  • application/msword: Word文档格式**
  • application/octet-stream:** 二进制流数据(如常见的文件下载)
  • application/x-www-form-urlencoded: 表单提交

4.3.2 HTTP请求方法

GET: GET方法用来请求访问已被URI识别的资源

  • a、 指定的资源经服务器端解析后返回响应内容如下图所示:
    get.png
  • b、 GET方法也可以用来提交表单和其他数据例如http://localhost/login.php?username=aa&password=1234从上面的URL请求中,很容易就可以辩认出表单提交的内容同时,浏览器对于提交URL的长度也有所限制。

POST:POST方法与GET功能类似,一般用来传输实体的主体。POST方法的主要目的不是获取响应主体的内容,而是向服务器提交数据。具体的响应如下:

post.png

PUT:从客户端向服务器传送的数据取代指定的文档的内容。

  • a、 PUT方法与POST方法最大的不同是: PUT是幂等的,而POST是不幕等的。具体就是一般PUT用来更新及覆盖原有的数据。
  • b、 我们更多时候将PUT方法用作传输资源

HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头。这个方法主要用于测试一些超链接的有效性,例如去网上下载的一下超链接探测软件就是使用的这个方法。

DELETE: 请求服务器删除指定资源。因为其不做安全性验证,现在基本上不使用这个方法。

OPTIONS:用来查询针对请求URI指定的资源支持的方法。

TRACE: 回显服务器收到的请求,主要用于测试或诊断,因为其安全性漏洞现在也基本上不使用这个方法。

CONNECT: 开启一个客户端与所请求资源之间的双向沟通的通道,它可以用来创建隧道。经常用于HTTP代理,代理服务器就是用的这个方法。

4.3.3 状态码

状态码是用以表示网页服务器超文本传输协议响应状态的3位数字代码,它报文中的具体位置如下图:

状态码.png

  • 状态码会根据数字代码的第一位数字来表示不同的状态,如下图所示:

状态码汇总.png

  • 以2开头的常见状态码

常见状态码1.png

  • 以3开头的常见状态码

常见状态码2.png

  • 以4开头的常见状态码

常见状态码3.png

  • 以5开头的常见状态码

常见状态码4.png

4.3.4 HTTP状态管理

Cookie:Cookie实际上是一小段的文本信息。

  • a、 客户端请求服务器,如果服务器需要记录该用户状态,就向客户端浏览器颁发一个Cookie。
  • b、 客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie-同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。具体的运作机制如下:

cookie.png
c、 Cookie的工作原理,这里我们用一张图片来说明。

Cookie工作原理.png

Session 是另一种记录客户状态的机制,保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。

  • a、 工作原理,这里我们用一张图片来说明:

Session.png

  • b、 保存Session ID的方法:采用Cookie保存、URL重写(例如:. http://.../xx;Sessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLeriQ99zWpBng!-145788764这种形式)、隐藏表单。
  • c、 有效时间:自动失效:超时时间;主动失效:调用HttpSession.Invalidate()方法;异常失效:服务器进程被停止。

Cookie和Session对比

  • a、存放位置不同Cookie存放在客户端,Session存放在服务端。
  • b、 安全性(隐私政策)的不同Cookie对客户端程序是课件的,里面的内容可能被窥探,修改和删除,而Session对客户端是透明的,不存敏感信息泄露的危险。
  • c、有效期上的不同Cookie的有效期可以设置为随意时间,可以保存很久,而Session服务器会定时去清理。
  • d、对服务器的压力不同:如果在并发量比较大的情况下,每个用户都会保存一个Session,这样会占用服务器大量的资源,而Cookie存放在客户端就不会这样。

4.4编码与解码

4.4.1字符集与编码

一套完整的编码规范分为字库表字符集编码方式三个部分如下:

字符集与编码.png
常见的编码规范有如下图:

编码规范.png

4.4.2 URL的编码和解码

  • URL采用ASCI字符集进行编码的,所以如果URL中含有非ASCI字符集中的字符,要对其进行编码
  • URL中一些保留字符,如"&"表示参数分隔符,如果想要在URL中使用这些保留字,那就需要编码
  • "%编码"规范:对URL中属于ASCI1字符集的非保留字不做编码;对URL中的保留字需要取其ASCI内码,然后加上"%"前缀将该字符进行编码;对于URL中的非ASCI字符需要取其Unicode内码,然后加上"%"前缀将该字符进行编码。

4.5 身份认证

HTTP现在常见的认证方式有BASIC认证(基本认证)DIGEST认证(摘要认证)SSL客户端认证FormBase认证(基于表单认证) 四种。

4.5.1 BASIC认证(基本认证)

他的认证方式是客户端与服务器之间进行的一种统一的认证方式,具体的表现形式如下图:

BASIC认证表现形式.png
这种认证方式在现在很多网站还很常见,他的具体认证流程如下:

BASIC认证.png

4.5.2 DIGEST认证(摘要认证)

为弥补BASIC认证存在的弱点,从HTTP/1.1起就有了DIGEST认证。DIGEST认证同样使用质询/响应的方式,但不会像BASIC认证那样直接发送明文密码。改进后的认证过程如下图:

DIGEST认证.png

4.5.3 SSL客户端认证

SSL客户端认证是借由HTTPS的客户端证书完成认证的方式。凭借客户端证书认证,服务器可确认访问是否来自己登录的客户端。这个后面在讲HTTPS详细介绍。

4.5.4 FormBase认证(基于表单认证)

这种认证方式是现在最为普遍的认证方式。

  • 基于表单的认证方法并不是在HTTP协议中定义的。
  • 使用由Web应用程序各自实现基于表单的认证方式。
  • 通过Cookie和Session的方式来保持用户的状态。

4.6 HTTP的特征

4.6.1 长连接和短连接

HTTP协议是基于请求/响应模式的,因此只要服务端给了响应,本次HTTP请求就结束了。HTTP的长连接和短连接本质上是TCP长连接和短连接。

  • HTTP 1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,结束就中断。短连接的建立过程如下: 短连接:建立连接一数据传输一关闭连接建立连接数据传输一关闭连接
  • HTTP 1.1起,默认使用长连接,用以保持连接特性。长连接的建立过程如下: 长连接:建立连接数据传输..(保持连接) .数据传输--关闭连接

4.6.2 HTTP中介

  • web代理:对于客户端来说扮演的是服务端的角色,对于服务端来说扮演的是客户端的角色。

代理1.png

  • 代理服务器的作用 a、 抓包:例如Fiddler就是采用的代理,也可以叫做网络拦截器。 b、 FQ:可以通过代理服务器访问外网,注意这里和VPN的原理不一样,VPN采用的是隧道协议,有兴趣可以去了解一下,下面以一幅图来表示FQ的过程。

代理2.png
c、 匿名访问:因为代理服务器不会对用户信息进行保存,这样子通过代理服务器进行访问其他网站,就可以增加隐秘性和安全性。 d、 过滤器:因为HTTP自身没有记忆性,没有识别能力,什么都可以传输有些特定的情况下需要过滤掉一下无用的信息,就需要使用web代理来过滤。例如下面的场景:

代理3.png

  • 网关:网关可以作为某种翻译器使用,它抽象出了一种能够到达资源的方法。网关是资源和应用程序之间的粘合剂。网关扮演的是"协议转换器"的角色。网关代理的对比,如下:

网关.png
由上图可以看出,web网关在一侧使用HTTP协议,在另一侧使用另一种协议。由上面也可以将网关分成两种:定义格式:<客户端协议>/<服务器端协议> a、(HTTP/)服务器端网关:通过HTTP协议与客户端对话,通过其他协议与服务器通信。 b、(/HTTP)客户端网关:通过其他协议与客户端对话,通过HTTP协议与服务器通信。

  • 常见的网关类型 a、 (HTTP/*)服务器端Web网关:这个就和上面图中画的那种,客户端发的是HTTP请求,网关将HTTP请求转换成其他协议发给服务器。 b、 (HTTP/HTTPS)服务器端安全网关:这个网关是客户端发送一个HTTP请求,网关进行加密然后传给服务器。 c、 (HTTPS/HTTP)客户端安全加速器网关:这个与上面对应着,客户端给服务器发送的是安全的HTTPS协议这样就需要这个网关来解密,解密后再发给服务器。 d、 资源网关:一些资源转换的网关。

4.6.3 缓存

缓存头部字段

  • Cache-Control请求/响应头,缓存控制字段 no-store:所有内容都不缓存。 no-cache:缓存,但是浏览器使用缓存前,都会请求服务器判断缓存资源是否是最新。 max-age=x(单位秒):请求缓存后的X秒不再发起请求。 s-maxage=x(单位秒):代理服务器请求源站缓存后的x秒不再发起请求,只对CDN缓存有效。 public:客户端和代理服务器(CDN)都可缓存。 private:只有客户端可以缓存。

  • Expires响应头,代表资源过期时间,由服务器返回提供,是http1.0的属性在与max-age共存的情况下,优先级要低。

  • ast-Modified响应头,资源最新修改时间,由服务器告诉浏览器。

  • if-Modified-Since请求头,资源最新修改时间,由浏览器告诉服务器,和LastModified是一对,它两会进行对比。

  • Etag响应头,资源标识,由服务器告诉浏览器。

  • f-None-Match请求头,缓存资源标识,由浏览器告诉服务器(其实就是上次服务器的Etag), 和Etag是一对,它两会进行对比。

缓存的工作方式

  • 情景一:让服务器与浏览器约定一个文件过期时间-Expires。如下形象的对话: 客户端:服务器服务器,我现在需要一个fjs文件,帮我找找, 然后给我。 服务器:次次找我要,烦不烦,文件给你可以,我们约定个寸间(Expires), 时间没到别来烦我了,返回了fjsl及过期时间Expires。

  • 场景二:让服务器与浏览器在约定文件过期时间的基础上,再加一个文件最新修改时间的对比Last-Modified与ifModified-Since.如下形象的对话: 客户端:服务器服务器,我现在需要一个fjs,你找到了给我顺便给我个过期时间,时间没到我保证不烦你。 服务器:行,过期时间我给你,另外再给你一个文件最新修改时间LastModified,到时候文件过期了咱两核对文件修改时间,对得上你就别烦我,返回a.js+ Expires+LastModified。

  • 场景三:让服务器与浏览器在过期时间Expires+ Last-Modified的基础上,增加一个文件内容唯一对比标记-Etag与lf-NoneMatch, Expires不稳定,再加入一个max-age来加以代替。如下形象的对话: 客户端:服务器服务器,我要什么你最懂得 服务器:我不懂!fjs我给你,过期时间我也给你,再给你一个max-age=60单位秒), Last-Modified你也给我收好,再加一个文件内容唯一标识符Etag。

缓存的改进方案

  • md5/hash缓存:通过不缓存html,为静态文件添加MD5或者hash标识,解决浏览器无法跳过缓存过期时间主动感知文件变化的问题。
  • CDN缓存:CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。

另外浏览器上的一些操作,对缓存也有影响,如下图可以看出具体的影响:

缓存.png

4.6.4 内容协商机制

我们访问谷歌,都是使用一个URL当我们在国内访问的时候是中文,但是当我们在国外访问时却是英文,这里面就使用到了内容协商机制。

内容协商机制:指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言,字符集,编码方式等作为判断的基准。

内容协商的方式

  • 客户端驱动 客户端发起请求,服务器发送可选项列表,客户端作出选择后在发送第二次请求。
  • 服务器驱动 服务检查客户端的请求头部集并决定提供哪个版本的页面。
  • 透明协商 某个中间设备(通常是缓存代理)代表客户端进行协商。

请求首部集:我们在实现内容协商的时候往往是通过特定的字段来进行协商的,如下:

  • Accept:告知服务器发送何种媒体类型
  • Accept-Language:告知服务器发送何种语言
  • Accept-Charset:告知服务器发送何种字符集
  • Accept-Encoding:告知服务器采用何种编码

4.6.5 断点续传与多线程下载

HTTP是通过在Header里两个参数实现的,客户端发请求时对应的是Range,服务器端响应时对应的是Content-Range

  • Range:用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式: Range:(unit=first byte pos)-[last byte pos](例子:Range: bytes=0-499)

  • Content-Range:用于响应头中,在发出带Range的请求后,服务器会在Content-Range头部返回当前接受的范围和文件总大小。一般格式为: Content-Range: bytes (unit first byte pos) - [last bytepos]/[entity legth] 但是在响应完成后,返回的响应头内容也不同: HTTP/1.1 200 Ok(不使用断点续传方式) HTTP/1.1 206 Partial Content (使用断点续传方式)

断点续传的过程

  • 户端一个1024K的文件,已经下载了其中512K。
  • 网络中断,客户端请求续传,因此需要在HTTP头中申明本次需要续传的片段:Range:bytes=512000-这个头通知服务端从文件的512K位置开始传输文件。
  • 服务端收到断点续传请求,从文件的512K位置开始传输,并在HТТР头部增加:Content-Range:bytes 512000-/1024000并且此时服务端返回的HTTP状态码应该是206,而不是200.

五 加强安全HTTPS

5.1 HTTPS协议概述

Https其实就等于HTTP+TLS,而TSL(传输层加密协议) = 加密+完整性保护+认证的方法解。所以HTTPS其实质就是在HTTP的应用层与运输层之间加了一层SSL/TLS,如下图:

HTTPS.png

5.2 HTTPS功能介绍

  • 加密

1、首先服务端通过非对称加密算法生成一对密钥:公钥和私钥; 2、服务端把公钥发送给客户端,私钥自己保存; 3、客户端收到公钥后,利用公钥对对称加密使用的密钥S进行加密得到T,然后再发送给服务端; 4、服务端收到T后,利用私钥解密T,得到密钥S; 5、这样客户端和服务端都拥有了对称加密使用的密钥S,在之后的通信过程中就使用对称加密进行。

  • 完整性保护(采用数字签名)

1、服务端在发送报文之前,先用散列值生成算法生成报文的消息摘要,然后再用私钥加密消息摘要生成数字签名,把数字签名与报文一起发送给客户端; 2、客户端接收到报文和数字签名后,先用相同的散列值生成算法重新计算报文的消息摘要,然后再用公钥解密数字签名得到报文的消息摘要,然后比较两份消息摘要是否相同,如果相同,说明报文在中途没有被篡改过,否则被篡改过;

  • 身份认证(采用数字证书)

1、服务端的运营人员向CA提交自己的公钥、组织信息、域名等信息,然后CA会通过各种渠道、各种手段来判断服务端的身份是否真实,是否合法等(在这里就可以杜绝中间人非法申请证书); 2、服务端的身份审核通过后,CA就会把服务端的公钥和证书的其他信息通过散列值算法生成一个消息摘要,然后用CA的私钥对消息摘要进行签名生成数字签名,然后把数字签名放入证书中,然后把这个证书颁发给服务端,所以数字证书最终包含服务端公钥 + 证书的数字签名 + 证书的其他信息。 3、现在服务端拿到了数字证书,客户端第一次请求服务端时,服务端就会把这个数字证书发送给客户端,客户端收到数字证书后,就会用CA的公钥对数字证书进行数字签名的验证,如果验证通过,说明数字证书中途没有被篡改过,是可靠的,从而知道数字证书中的公钥也是可靠的,所以客户端就放心的取出数字证书中的公钥进行以后的HTTPS通信。 4、在对数字证书的数字签名进行验证之前,必须先知道CA的公钥,因为数字证书的数字签名是由CA的私钥进行签名的,所以CA的公钥必须安全的转交给客户端,如何安全的转交又是一个困难的问题,所以,大多数浏览器开发商发布版本时,会事先在内部植入常用的CA机构的根证书, 这些根证书中会包含CA的公钥,也就是说CA的公钥已经内置在浏览器中了。

下面我给出一个图片来描述具体的流程:

HTTPS流程.png

5.3 HTTPS的成本与性能影响

成本

  • 证书费用以及更新维护
  • HTTPS降低用户访问速度
  • 消耗CPU资源,需要增加大量机器

性能影响

  • 协议交互所增加的网络RTT 首先看看HTTP交互的图:

HTTP时间.png
由上图可知HTTP请求只需要进行一个TCP3次握手的时间就可以进行数据传输,下面我们看看HTTPS交互需要的耗时:

HTTPS时间.png
上图是HTTPS最多需要进行的步骤,最多可能增加7个RTT。

  • 加解密相关的计算耗时:这个主要包括客户端计算耗时和服务器端计算耗时。

六 拓展HTTP

6.1 WebSocket

WebSocket是基于HTTP协议的(借用HTTP的握手),他是建立在TCP 协议之上,服务器端的实现比较容易。与HTTP协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用HTTP协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。数据格式比较轻量,性能开销小,通信高效。可以发送文本,也可以发送二进制数据。没有同源限制,客户端可以与任意服务器通信。协议标识符是ws(如果加密,则为wss),服务器网址就是URL。

  • 下面我们看看WebSocket的握手:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

可以发现,这段类似HTTP协议的握手请求中,多了这么几个东西。

Upgrade: websocket
Connection: Upgrade

这个就是WebSocket的核心了,告诉ApacheNginx等服务器:注意啦,我发起的请求要用 WebSocket协议,快点帮我找到对应的助理处理~而不是那个老土的HTTP。

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

首先Sec-WebSocket-Key是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:我要验证你是不是真的是WebSocket助理。 然后Sec_WebSocket-Protocol是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。 最后Sec-WebSocket-Version是告诉服务器所使用的WebSocket Draft(协议版本),在最初的时候,WebSocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么 Firefox 和 Chrome 用的不是一个版本之类的,当初WebSocket协议太多可是一个大难题。然后服务器会返回下列东西,表示已经接受到请求, 成功建立WebSocket!

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

这里开始就是 HTTP 最后负责的区域了,告诉客户端,我已经成功切换协议。

Upgrade: websocket
Connection: Upgrade

依然是固定的,告诉客户端即将升级的是WebSocket协议,而不是mozillasocketlurnarsocket或者shitsocket。然后,Sec-WebSocket-Accept这个则是经过服务器确认,并且加密过后的Sec-WebSocket-Key。服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。后面的,Sec-WebSocket-Protocol则是表示最终使用的协议。至此,HTTP 已经完成它所有工作了,接下来就是完全按照WebSocket协议进行了。下面我用一张图来表示WebSocket的运行机理:

webSocket.png

监听服务器状态

  • ajax轮询:ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
  • long poll:其实原理跟ajax轮询差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起请求后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。

这一小节只是粗略的介绍了一下WebSocket的相关知识,后面有机会再详细探讨webSock。

6.2 WebDAV

6.2.1 概述

WebDAV(Web-based Distributed Authoring and Versioning) 一种基于HTTP 1.1协议的通信协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。

webDav.png

6.2.2 WebDAV追加的方法(相对于HTTP),如下图:

wenDav追加的方法.png

6.2.3 WebDAV新增de状态码(相对于HTTP),如下图:

webDav增加的状态码.png

6.2.4 WebDAV的使用场景

WebDAV一般使用在NASOffice和WPS这种多终端同步的编辑软件同步的功能会用到。现在基本上的WebDAV的使用都可以被ftp所取代,另外在有些利益的情况下,使得WebDAV得不到发展,这里就不过多的介绍了。

6.3 QUIC(HTTP 3.0)

6.3.1 概述

前面提到的HTTP2.0虽然大大增加了并发性,但还是有问题的。因为HTTP 2.0也是基于TCP协议的,TCP协议在处理包时是有严格顺序的。当其中一个数据包遇到问题,TCP连接需要等待这个包完成重传之后才能继续进行。虽然HTTP 2.0通过多个stream,使得逻辑上一个TCP连接上的并行内容,进行多路数据的传输,然而这中间并没有关联的数据。一前一后,前面stream 2的帧没有收到,后面stream 1的帧也会因此阻塞,于是,就又到了从TCP切换到UDP。下图我们看看HTTP几个版本的对比:

HTTP对比.png

6.3.2 特点

  • 机制一:自定义连接机制 一条TCP连接是由四元组标识的,分别是源IP源端口目的IP目的端口。一旦一个元素发生变化时,就需要断开重连,重新连接。在移动互联情况下,当手机信号不稳定或者在WIFI和 移动网络切换时,都会导致重连,从而进行再次的三次握手,导致一定的时延。 这在TCP是没有办法的,但是基于UDP,就可以在QUIC自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个64位的随机数作为ID来标识,而且UDP是无连接的,所以当IP或者端口变化的时候,只要ID不变,就不需要重新建立连接。

  • 机制二:自定义重传机制 TCP为了保证可靠性,通过使用序号应答机制,来解决顺序问题丢包问题。任何一个序号的包发过去,都要在一定的时间内得到应答,否则一旦超时,就会重发这个序号的包。那怎么样才算超时呢?这个超时是通过采样往返时间RTT通过自适应重传算法不断调整的。 其实,在TCP里面超时的采样存在不准确的问题例如,发送一个包,序号为100,发现没有返回,于是再发送一个100,过一阵返回一个ACK101。这个时候客户端知道这个包肯定收到了,但是往返时间是多少呢?是ACK到达的时间减去后一个100发送的时间,还是减去前一个100发送的时间呢?事实是,第一种算法把时间算短了,第二种算法把时间算长了。 QUIC也有个序列号,是递增的。任何一个序列号的包只发送一次,下次就要加一了。例如,发送一个包,序号是100,发现没有返回;再次发送的时候,序号就是101了;如果返回的ACK 100,就是对第一个包的响应。如果返回ACK 101就是对第二个包的响应,RTT计算相对准确。但是这里有一个问题,就是怎么知道包100和包101发送的是同样的内容呢?QUIC定义了一个offset概念。QUIC既然是面向连接的,也就像TCP一样,是一个数据流,发送的数据在这个数据流里面有个偏移量offset,可以通过offset查看数据发送到了哪里,这样只要这个offset的包没有来,就要重发;如果来了,按照offset拼接,还是能够拼成一个流。

自定义重传.png

  • 机制三:无阻塞的多路复用 有了自定义的连接和重传机制,我们就可以解决上面HTTP 2.0的多路复用问题。同HTTP 2.0一样,同一条QUIC连接上可以创建多个stream,来发送多个HTTP请求。但是,QUIC是基于UDP的,一个连接上的多个stream之间没有依赖。这样,假如stream2丢了一个UDP包,后面跟着stream3的一个UDP包,虽然stream2的那个包需要重传,但是stream3的包无需等待,就可以发给用户。

多路复用.png

  • 机制四:自定义流量控制 TCP的流量控制是通过滑动窗口协议QUIC的流量控制也是通过window_update,来告诉对端它可以接受的字节数。但是QUIC的窗口适应自己的多路复用机制的,不但在一个连接上控制窗口 ,还在一个连接中的每个stream控制窗口。 在TCP协议中,接收端的窗口的起始点是下一个要接收并且ACK的包,即便后来的包都到了,放在缓存里面 ,窗口也不能右移,因为TCP的ACK机制是基于序列号的累计应答,一旦ACK了一个系列号,就说明前面的都到了,所以只要前面的没到,后面的到了也不能ACK,就会导致后面的到了,也有可能超时重传,浪费带宽。 QUIC的ACK基于offset的,每个offset的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空挡会等待到来或者重发即可,而窗口的起始位置为当前收到的最大offset,从这个offset到当前的stream所能容纳的最大缓存,是真正的窗口大小。显然,这样更加准确。

QUIC滑动窗口.png

6.3.3 总结

  • HTTP协议虽然很常用,也很复杂,重点记住GETPOSTPUTDELETE这几个方法,以及重要的首部字段

  • HTTP 2.0通过头压缩分帧二进制编码多路复用等技术提升性能;

  • QUIC协议通过基于UDP 自定义的类似TCP的连接重试多路复用流量控制技术,进一步提升性能。

七 总结

本文是在我去看了OKHttp源码的基础上,发现自己在HTTP基础上的薄弱点,然后去学习了整个HTTP的协议族。基本上包含了所有有关HTTP的基础知识点,当然一些具体的算法相关的知识点本文没过多的去介绍,还有HTTP安全相关的本文也没去介绍,后面有机会专门写篇博客来介绍web安全和HTTP相关的算法。这篇文章借鉴了大话HTTP课程和去谈网络协议专栏里面的一些内容,还有一些优秀的博客、《HTTP权威指南》和《TCP/IP详解卷三》等优秀书籍的内容。学海无涯,多多努力。

参考资料

原文地址(我的个人博客):你真的掌握了HTTP吗?