HTTP 协议,几乎是每个人上网用的第一个协议,例如http://www.163.com 是个 URL,叫作统一资源定位符。之所以叫统一,是因为它是有格式的。
- HTTP 称为协议。
- www.163.com 是一个域名,表示互联网上的一个位置。
- 有的 URL 会有更详细的位置标识,例如 http://www.163.com/index.html 。
HTTP 请求的准备
浏览器会将 www.163.com 这个域名发送给 DNS 服务器,让它解析为 IP 地址,然后建立TCP连接(三次握手),目前使用的 HTTP 协议大部分都是 1.1,默认是开启了 Keep-Alive 的,这样建立的 TCP 连接,就可以在多次请求中复用。
HTTP 请求的构建
建立了连接以后,浏览器就要发送 HTTP 的请求
HTTP 的报文大概分为三大部分。第一部分是请求行,第二部分是请求的首部,第三部分才是请求的正文实体。
第一部分:请求行
由3部分组成,分别为:请求方法、URL 以及协议版本,之间由空格分隔
请求方法:包括GET、HEAD、PUT、POST、TRACE、OPTIONS、DELETE以及扩展方法,当然并不是所有的服务器都实现了所有的方法,部分方法即便支持,处于安全性的考虑也是不可用的
议版本的格式为:HTTP/主版本号.次版本号,常用的有HTTP/1.0和HTTP/1.1
HTTP1.1 中的请求方式:
方法 | 作用 |
---|---|
GET | 请求获取由 Request-URI 所标识的资源 |
POST | 请求服务器接收在请求中封装的实体,并将其作为由 Request-Line 中的 Request-URI 所标识的资源的一部分 |
HEAD | 请求获取由 Request-URI 所标识的资源的响应消息报头 |
PUT | 请求服务器存储一个资源,并用 Request-URI 作为其标识符 |
DELETE | 请求服务器删除由 Request-URI 所标识的资源 |
TRACE | 请求服务器回送到的请求信息,主要用于测试或诊断 |
CONNECT | 保留将来使用 |
OPTIONS | 请求查询服务器的性能,或者查询与资源相关的选项和需求 |
第二部分:首部字段
请求行下面就是首部字段。首部是 key value,通过冒号分隔。
常见请求头如下:
请求头 | 说明 |
---|---|
Host | 接受请求的服务器地址,可以是IP:端口号,也可以是域名 |
User-Agent | 发送请求的应用程序名称 |
Connection | 指定与连接相关的属性,如Connection:Keep-Alive |
Accept-Charset | 通知服务端可以发送的编码格式 |
Contnet-Type | 正文的格式,如JSON |
Cache-control | 控制缓存 |
If-Modified-Since | 如果服务器的资源在某个时间后更新了,客户端就应该下载最新下资源 |
第三部分:请求正文
可选部分,比如GET请求就没有请求正文
几个示例
GET请求示例:
POST请求示例:
HTTP 返回的构建
1,状态行
状态行由3部分组成,分别为:协议版本,状态码,短语,之间由空格分隔
状态码为3位数字反应HTTP请求的结果。200意味着大吉大利;404意味着服务端无法响应这个请求。
几个常见的状态码
状态码 | 说明 |
---|---|
200 | 响应成功 |
301 | 永久重定向,搜索引擎将删除源地址,保留重定向地址 |
302 | 暂时重定向,重定向地址由响应头中的Location属性指定(JSP中Forward和Redirect之间的区别)由于搜索引擎的判定问题,较为复杂的URL容易被其它网站使用更为精简的URL及302重定向劫持 |
304 | 缓存文件并未过期,还可继续使用,无需再次从服务端获取 |
400 | 客户端请求有语法错误,不能被服务器识别 |
403 | 服务器接收到请求,但是拒绝提供服务(认证失败) |
404 | 请求资源不存在 |
500 | 服务器内部错误 |
短语大概说明一下原因。
2,响应头部
与请求头部类似,为响应报文添加了一些附加信息,key value形式
- Retry-After:客户端应在多长时间后再次尝试一下。503指服务暂时不再和这个值配合使用。
- Content-Type:可以是HTML,或JSON。
响应示例:
构造好了返回的 HTTP 报文,接下来就是把这个报文发送出去。还是交给 Socket 去发送,还是交给 TCP 层,让 TCP 层将返回的 HTML,也分成一个个小的段,并且保证每个段都可靠到达。
HTTP 2.0
HTTP 1.1 在应用层以纯文本的形式进行通信。每次通信都要带完整的 HTTP 的头,而且不考虑 pipeline 模式的话,每次的过程总是像上面描述的那样一去一回。这样在实时性、并发性上都存在问题。
为了解决这些问题,HTTP 2.0通过头压缩、分帧、二进制编码、多路复用等提升性能。
- 对HTTP的头进行一定的压缩,将原来每次都要携带的大量key value在两点建立一个索引表,对相同的头只发送索引表中的索引。
- 将一个TCP的连接中,切分成多个流,每个流都有自己的ID。它是一个虚拟通道,有优先级。将所有传输信息分割为更小的消息和帧,并采用二进制编码。如Header帧,Data帧,多个Data帧属于同一个流。
通过这两种机制,HTTP 2.0 的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据
举一个例子
假设我们的一个页面要发送三个独立的请求,一个获取 css,一个获取 js,一个获取图片 jpg。如果使用 HTTP 1.1 就是串行的,但是如果使用 HTTP2.0,就可以在一个连接里,客户端和服务端都可以同时发送多个请求或回应,而且不用按照顺序一对一对应。
HTTP 2.0 其实是将三个请求变成三个流,将数据分成帧,乱序发送到一个 TCP 连接中。
- 成功解决了HTTP 1.1的队首阻塞问题,也不需要通过HTTP 1.x的pipeline机制用多条TCP连接实行并行请求与响应;
- 减少了 TCP连接数对服务器性能的影响,同时将页面的多个数据 css、js、 jpg 等通过一个数据链接进行传输,能够加快页面组件的传输速度。
QUIC 协议的"城会玩"
HTTP 2.0 虽然大大增加了并发性,但还是有问题的。因为 HTTP 2.0 也是基于 TCP 协议的,TCP 协议在处理包时是有严格顺序的。当其中一个数据包遇到问题,TCP 连接需要等待这个包完成重传之后才能继续进行。前面的stream 2的帧没有收到,后面的stream 1也会阻塞。
QUIC 通过UDP,自定义类似的TCP的连接、重试、多路复用、流量控制,进一步提升性能。
Google 的 QUIC 协议,它是如何“城会玩”的。
机制一:自定义连接机制
基于UDP,在QUIC自己的逻辑中维护连接的机制,不再以四元组标识,以64位的随机数作为ID来标识。 UDP 是无连接的,所以当 IP 或者端口变化的时候,只要 ID 不变,就不需要重新建立连接。
机制二:自定义重传机制
QUIC 也有个序列号,是递增的。任何一个序列号的包只发送一次,下次就要加一了。例如,发送一个包,序号是 100,发现没有返回;再次发送的时候,序号就是 101 了;如果返回的 ACK 100,就是对第一个包的响应。如果返回 ACK 101 就是对第二个包的响应,RTT 计算相对准确。
但是这里有一个问题,就是怎么知道包 100 和包 101 发送的是同样的内容呢?QUIC 定义了一个 offset 概念。
QUIC 既然是面向连接的,也就像 TCP一样,是一个数据流,发送的数据在这个数据流里面有个偏移量 offset,可以通过 offset 查看数据发送到了哪里,这样只要这个 offset 的包没有来,就要重发;如果来了,按照 offset 拼接,还是能够拼成一个流。
图中 左边原TCP重传,右边QUIC重传
机制三:无阻塞的多路复用
同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求。但是,QUIC 是基于 UDP 的,一个连接上的多个 stream 之间没有依赖。这样,假如 stream2 丢了一个 UDP 包,后面跟着 stream3 的一个 UDP 包,虽然 stream2 的那个包需要重传,但是 stream3 的包无需等待,就可以发给用户。
机制四:自定义流量控制
TCP 的流量控制是通过滑动窗口协议。
QUIC 的流量控制也是通过 window_update,来告诉对端它可以接受的字节数。但是 QUIC 的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个 stream 控制窗口。
QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空挡会等待到来或者重发即可,而窗口的起始位置为当前收到的最大 offset,从这个 offset 到当前的 stream 所能容纳的最大缓存,是真正的窗口大小。显然,这样更加准确。
另外,还有整个连接的窗口,需要对于所有的 stream 的窗口做一个统计
小结
- HTTP 协议虽然很常用,也很复杂,重点记住 GET、POST、 PUT、DELETE 这几个方法,以及重要的首部字段;
- HTTP 2.0 通过头压缩、分帧、二进制编码、多路复用等技术提升性能;
- QUIC 协议通过基于 UDP 自定义的类似 TCP 的连接、重试、多路复用、流量控制技术,进一步提升性能。
参考资料:
趣谈网络协议(极客时间)链接:
http://gk.link/a/106nW
HTTP基本原理(格式详解):
https://blog.csdn.net/hudashi/article/details/50789006
HTTP请求、响应报文格式:
https://blog.csdn.net/a19881029/article/details/14002273
GitHub链接:
https://github.com/lichangke/LeetCode
知乎个人首页:
https://www.zhihu.com/people/lichangke/
CSDN首页:
https://me.csdn.net/leacock1991
欢迎大家来一起交流学习