HTTP(HyperText Transfer Protocol,超文本传输协议又叫超文本转移协议)协议是用来完成从客户端到服务器等一系列运行流程的协议,Web是建立在HTTP协议上通信的。
HTTP的诞生
早期的互联网(20世纪80年代末,90年代初)是少数人的,那时候人们想要实现一种远隔两地的研究员共享知识的设想,所以相继提出了3项(到目前为止)WWW构建技术:用SGML(Standard Generalized Markup Language,标准通用标记语言)作为页面的文本标记语言的HTML、作为文档传递协议的HTTP、指定文档所在地址的URL。
注:www现在统称web。
众所周知上世纪90年代初是互联网发展的高发期,java、JavaScript、HTML、HTTP等语言和规范的诞生,浏览器大战等等一系列事件好像都是那个时代发生的。而我们的HTTP协议也是在那个时代诞生的。
HTTP/0.9
HTTP于1990年问世,那时HTTP还没有正式的被标准化,HTTP/1.0还没有发布,所以我们称1.0之前的那个版本为0.9版。
该版本很简单,只有一个GET命令,返回数据只能是HTML。
HTTP/1.0
1996年5月,HTTP/1.0 版本发布,内容大大增加。
- 任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。
- 除了GET命令,还引入了POST/PUT/HEAD/DELETE命令,丰富了浏览器与服务器的互动手段。
- HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。
- 其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。
请求格式的改变
HTTP/0.9时的GET请求:
HTTP/1.0时的GET请求:
响应格式的改变
HTTP/0.9时的响应:
HTTP/1.0时的响应:
Content-Type字段
关于字符的编码,HTTP/1.0版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。
MIME Type
text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
application/pdf
application/zip
application/atom+xml
上面的Content-Type类型总称为MIME type,每个值包括一级类型和二级类型,之间用斜杠分隔。
除了预定义的类型,厂商也可以自定义类型。
application/vnd.debian.binary-package
上面的类型表明,发送的是Debian系统的二进制数据包。
MIME type还可以在尾部使用分号,添加参数。
Content-Type: text/html; charset=utf-8
上面的类型表明,发送的是网页,而且编码是UTF-8。
客户端请求的时候,可以使用Accept字段声明自己可以接受哪些数据格式。
Accept: */*
上面代码中,客户端声明自己可以接受任何格式的数据。
Content-Encoding字段
由于发送的数据可以是任何格式,因此可以把数据压缩后再发送。Content-Encoding字段说明数据的压缩方法。
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法。
Accept-Encoding: gzip, deflate
状态码
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是否正确处理了请求。
状态码如200 OK,以3位数字和原因短句组成。
其中数字中的第一位指定了响应类别,后两位无分类。
状态码类别:
1xx:接受的请求正在处理。
2xx:请求正常处理。
3xx:需要进行附加操作以完成请求。
4xx:服务器无法处理请求。
5xx:服务器处理请求出错。
2XX
200 OK:正常处理请求。
204 No Content:请求正常处理,但是没有任何资源返回。
206 Partial Content:请求资源的某一部分。
3XX
301 Move Permanently:永久性重定向。
302 Found:临时性重定向。
303 See Other:请求对应的资源存在着另一个请求URI,应使用GET方法定向请求资源。303状态码和302状态码有着相同功能,但303状态码明确表示客户端应当采用GET方法获取资源,这点与302状态码有区别。
304 Not Modified:客户端发送附带条件的请求(指采用GET方法的请求报文中包含if-Match,if-Modified-Since,if-Node-Match,if-Range,if-Unmodified-Since 中的任一首部)时,服务器准许访问,但是请求条件不满足,直接返回304 Not Modified(服务端资源未改变,可直接使用客户端未过期的缓存)。304状态码返回时,不包含任何响应的主体部分。304虽然被划分在3XX类别中,但是和重定向没有关系。
307 Temporary Redirect:临时重定向,与302的区别就是,302标准禁止POST转为GET,但是大家不遵守。307严格按标准POST。
4XX
400 Bad Request:请求报文中存在语法错误。
401 Unauthorized:请求需要HTTP认证。
上图为使用 Visual SVN 在浏览器中弹出的认证框。
403 Forbidden:请求被拒绝,服务器端没有必要给出拒绝的详细理由,但如果想作说明的话,可以在实体的主体部分对原因进行描述,这样用户就可以看到了。未获得文件系统的访问授权,访问权限出现某些问题(从未授权的发送源IP地址试图访问)等列举的情况都可能是发生403的原因。
404 Not Found:没有在服务器上找到该资源,一般为url书写错误或者就是没有该资源。
5XX
500 Internet Server Error:服务端在执行请求时发生了错误,代码有问题。
503 Service Unavailable:服务器暂时处于超负载或正在进行停机维护。
注意:1.对于处理响应时的行为,每种浏览器有可能出现的不同的情况。
2.以上关于状态码不确定是不是全是1.0版本的,可能有些为1.1版本的。
HTTP/1.0的缺点
HTTP/1.0 版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。
TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。所以,HTTP 1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。
Connection: keep-alive
这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。
Connection: keep-alive
一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。
HTTP/1.1
1997年1月,HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了HTTP 协议,一直用到了20年后的今天,直到现在还是最流行的版本。
相比HTTP/1.0,1.1版本引入了持久链接、管道机制、Content-Length 字段、分块传输编码、新增了OPTIONS/TRACE/CONNECT命令。
持久链接
HTTP/1.1 版的最大变化,就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive
客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。
Connection: close
目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。
补充:浏览器最大并发限制数
大部分浏览器允许同域名下最大的并发数量是6个(这可能在前端优化中涉及到),所以我们可以通过多域名缓存的方式来突破浏览器同域名最大并发限制数量的限制来做前端优化。
多域名缓存的其他优点:
- CDN缓存更加方便
- 突破浏览器并发限制
- 节约Cookie的带宽
- 防止不必要的安全问题
- 解决主机域名连接数,优化页面响应速度
用图来直观的表示一下HTTP/1.1的持久链接和没有持久链接的HTTP协议:
管道机制
HTTP/1.1 版还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。
举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。
一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度。
Content-Length: 3495
上面代码告诉浏览器,本次回应的长度是3495个字节,后面的字节就属于下一个回应了。
在HTTP/1.0版中,Content-Length字段不是必需的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了。
分块传输编码
使用Content-Length字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。
对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用"流模式"(stream)取代"缓存模式"(buffer)。
因此,1.1版规定可以不使用Content-Length字段,而使用“分块传输编码”(chunked transfer encoding)。只要请求或回应的头信息有Transfer-Encoding字段,就表明回应将由数量未定的数据块组成。
Transfer-Encoding: chunked 每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了。
Cookie
HTTP是一种无状态的协议 。
无状态的优点:
- 减少服务器的CPU内存资源的消耗。
- 使得HTTP协议本身是非常简单的,被广泛应用。
但是有时是需要保存状态的,比如登录认证的web页面,如果没有状态保存,那么每一次跳转页面岂不是都要登录或者通过请求报文中附加参数来管理会话。所以为了实现期望保存状态的功能,于是引入了Cookie技术。当然Cookie也不一定只保存会话的状态,也可以保存一些其他的数据大小比较小的信息(话外:现在Cookie也令人各种诟病,于是Web Strong就应运而生了)。
Cookie技术通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
Cookie保存状态流程:
Cookie会根据服务器端发送的响应报文内的一个叫做Set-Cookie的首部字段信息,通知客户端保存cookie。当下次客户端在往该服务器发送请求时,客户端会自动请求报文中加入的cookie值后发送出去。
服务器端发现客户端发送过来的cookie后,会去检查究竟是从哪一个客户端发送来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
其他功能
HTTP/1.1版还新增了许多动词方法: OPTIONS、TRACE、CONNECT。
另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。
有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
HTTP/1.1的缺点
说了很多HTTP/1.1相对于HTTP/1.0新功能,但是随着时代的发展,技术的进步,它的确定越来越多的暴露出来。
比如:虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为“队头堵塞”(Head-of-line blocking)。
为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的。
这只是一点,后面还会讲到其他的一些缺点和不足。
HTTPS和SPDY
看一下左边的文章目录,接下来按说该讲一下HTTP/2了,但是在讲HTTP/2之前需要先讲一下HTTPS和SPDY的相关协议内容在去看HTTP/2就好理解了,所以先插入讲一下HTTPS和SPDY的相关知识。
HTTPS
为什么出现一个HTTPS呢?它和HTTP有啥异同呢?带着这两个问题,可以去看一下我的另一篇关于HTTPS见解的文章HTTPS基本认识
SPDY
关于SPDY的相关个人见解我也单独组织了一篇文章SPDY基本认识
HTTP/2
2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。
相比HTTP/1.1,HTTP/2版本引入了二进制协议、多工、数据流、头信息压缩、服务器推送。
其实看完前面的SPDY,其实HTTP/2也就差不多了,因为你会发现好多新机制就是基于SPDY的。
HTTP/2跟SPDY的区别:
- HTTP/2 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
- HTTP/2 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DELEFT
HTTP/2的优势
相比 HTTP/1.x,HTTP/2 在底层传输做了很大的改动和优化:
HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式。二进制格式在协议的解析和优化扩展上带来更多的优势和可能。
HTTP/2 对消息头采用 HPACK 进行压缩传输,能够节省消息头占用的网络的流量。而 HTTP/1.x 每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。头压缩能够很好的解决该问题。
多路复用,直白的说就是所有的请求都是通过一个 TCP 连接并发完成。HTTP/1.x 虽然通过 pipeline 也能并发请求,但是多个请求之间的响应会被阻塞的,所以 pipeline 至今也没有被普及应用,而 HTTP/2 做到了真正的并发请求。同时,流还支持优先级和流量控制。
Server Push:服务端能够更快的把资源推送给客户端。例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 再发送这些请求。当客户端需要的时候,它已经在客户端了。
HTTP/2 主要是 HTTP/1.x 在底层传输机制上的完全重构,HTTP/2 是基本兼容 HTTP/1.x 的语义的。Content-Type
仍然是 Content-Type
,只不过它不再是文本传输了。
后记
篇幅有些长,终于写完了,大部分摘自以下资料,做了个搬运工,顺便加上了部分自己的理解。
本文借鉴资料:
- 阮一峰的HTTP 协议入门
- 《图解HTTTP》
- 《HTML5程序设计》(第二版)