网络协议趣谈HTTP协议内容和传输
Posted sysu_lluozh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络协议趣谈HTTP协议内容和传输相关的知识,希望对你有一定的参考价值。
前面讲述完传输层,接下来从最常用的HTTP协议开始讲应用层的协议
HTTP协议,几乎是每个人上网用的第一个协议,同时也是很容易被人忽略的协议
比如看新闻的163网站http://www.163.com
http://www.163.com是个URL,叫作统一资源定位符。之所以叫统一,是因为它是有格式的
HTTP称为协议,www.163.com是一个域名,表示互联网上的一个位置。有的URL会有更详细的位置标识,例如http://www.163.com/index.html
正是因为这个东西是统一的,所以当把这样一个字符串输入到浏览器的框里时,浏览器才知道如何进行统一处理
一、HTTP请求的准备
浏览器会将www.163.com这个域名发送给DNS服务器,让它解析为IP地址。有关DNS的过程其实非常复杂,DNS服务器将域名解析成为IP地址。那接下来是发送HTTP请求吗?
不是的,HTTP是基于TCP协议的,当然是要先建立TCP连接,那怎么建立呢?这就需要TCP的三次握手
目前使用的HTTP协议大部分都是1.1。在1.1的协议中,默认是开启了Keep-Alive的,这样建立的TCP连接可以在多次请求中复用
TCP的三次握手和四次挥手还是很费时费力的,如果好不容易建立了连接,然后就做了一点儿事情就结束了,有点儿浪费人力和物力
二、HTTP请求的构建
建立了连接以后,浏览器就要发送HTTP的请求
请求的格式就像这样
HTTP的报文大概分为三大部分。第一部分是请求行,第二部分是请求的首部,第三部分才是请求的正文实体
2.1 第一部分:请求行
在请求行中,URL就是http://www.163.com,版本为HTTP 1.1,接下来特别说一下几种类型的方法
2.1.1 GET方法
对于访问网页来讲,最常用的类型就是GET,GET就是去服务器获取一些资源
对于访问网页来讲,要获取的资源往往是一个页面。其实也有很多其他的格式,比如说返回一个JSON字符串,到底要返回什么是由服务器端的实现决定的
例如,在云计算中如果服务器端要提供一个基于HTTP协议的API,获取所有云主机的列表,这就会使用GET方法得到,返回的可能是一个JSON字符串。字符串里面是一个列表,列表里面是云主机的信息
2.1.2 POST方法
另外一种类型叫做POST,它需要主动告诉服务端一些信息,而非获取
要告诉服务端什么呢?一般会放在正文里面,正文可以有各种各样的格式。常见的格式也是JSON
例如,在云计算中如果服务器端要提供一个基于HTTP协议的创建云主机的API,这就会用到POST方法,这个时候往往需要将要创建多大的云主机?多少CPU多少内存?多大硬盘?这些信息放在JSON字符串里面,通过POST的方法告诉服务器端
2.1.3 PUT方法
还有一种类型叫PUT,就是向指定资源位置上传最新内容
但是,HTTP的服务器往往是不允许上传文件的,所以PUT和POST就都变成了要传给服务器东西的方法
在实际使用过程中,这两者还会有稍许的区别。POST往往是用来创建一个资源,而PUT往往是用来修改一个资源
例如,云主机已经创建好了,想对这个云主机打一个标签,说明这个云主机是生产环境的,另外一个云主机是测试环境的。那怎么修改这个标签呢?往往就是用PUT方法
2.1.4 DELETE方法
再有一种常见的就是DELETE,顾名思义就是用来删除资源的
例如,要删除一个云主机,就会调用DELETE方法
2.2 第二部分:首部字段
请求行下面就是首部字段。首部是key value,通过冒号分隔。这里面往往保存了一些非常重要的字段
2.2.1 Accept-Charset
Accept-Charset,表示客户端可以接受的字符集。防止传过来的是另外的字符集从而导致出现乱码
2.2.2 Content-Type
Content-Type,指正文的格式。例如,进行POST的请求,如果正文是JSON, 那么应该将这个值设置为JSON
这里需要重点说一下的就是缓存。为啥要使用缓存呢?那是因为一个非常大的页面有很多东西
例如,浏览一个商品的详情,里面有这个商品的价格、库存、展示图片、使用手册等等。商品的展示图片会保持较长时间不变,而库存会根据用户购买的情况经常改变。如果图片非常大,而库存数非常小,如果每次要更新数据的时候都要刷新整个页面,对于服务器的压力就会很大
对于这种高并发场景下的系统,在真正的业务逻辑之前都需要有个接入层,将这些静态资源的请求拦在最外面
这个架构的图就像这样
对于静态资源,有Vanish缓存层。当缓存过期的时候,才会访问真正的Tomcat应用集群
2.2.3 Cache-control
在HTTP头里面,Cache-control是用来控制缓存的
当客户端发送的请求中包含max-age指令时,如果判定缓存层中,资源的缓存时间数值比指定时间的数值小,那么客户端可以接受缓存的资源,当指定max-age值为0,那么缓存层通常需要将请求转发给应用集群
2.2.4 If-Modified-Since
另外,If-Modified-Since也是一个关于缓存的
如果服务器的资源在某个时间之后更新了,那么客户端就应该下载最新的资源,如果没有更新,服务端会返回304 Not Modified的响应,那客户端就不用下载,从而节省带宽
到此为止,仅仅是拼凑起HTTP请求的报文格式,接下来浏览器会把它交给下一层传输层。怎么交给传输层呢?其实也无非是用Socket这些东西,只不过用的浏览器里,这些程序不需要自己写,别人已经实现好了
三、HTTP请求的发送
3.1 HTTP层通过stream二进制流发送给TCP层
HTTP协议是基于TCP协议的,所以它使用面向连接的方式发送请求,通过stream二进制流的 方式传给对方
3.2 TCP层封装地址信息交给IP层
当然,到了TCP层,它会把二进制流变成一个的报文段发送给服务器
在发送给每个报文段的时候,都需要对方有一个回应ACK,来保证报文可靠地到达了对方。如果没有回应,那么TCP这一层会进行重新传输直到可以到达。同一个包有可能被传了好多次,但是HTTP这一层不需要知道这一点,因为是TCP这一层在埋头苦干
TCP层发送每一个报文的时候,都需要加上自己的地址(即源地址)和它想要去的地方(即目标地址),将这两个信息放到IP头里面,交给IP层进行传输
3.3 IP层封装MAC信息发给MAC地址或网关
IP层需要查看目标地址和自己是否是在同一个局域网
如果是,就发送ARP协议来请求这个目标地址对应的MAC地址,然后将源MAC和目标MAC放入MAC头,发送出去即可
如果不在同一个局域网,就需要发送到网关,还需要发送ARP协议来获取网关的MAC地址,然后将源MAC和网关MAC放入MAC头,发送出去
网关收到包发现MAC符合,取出目标IP地址,根据路由协议找到下一跳的路由器,获取下一
跳路由器的MAC地址,将包发给下一跳路由器
这样路由器一跳一跳终于到达目标的局域网。这时最后一跳的路由器能够发现,目标地址就在自己的某一个出口的局域网上。于是,在这个局域网上发送ARP,获得这个目标地址的MAC地址,将包发出去
3.4 目标的机器的处理
- 发现MAC地址符合,就将包收起来
- 发现IP地址符合,根据IP头中协议项知道自己上一层是TCP协议
- 解析TCP的头中的序列号,需要看一看这个序列包是不是想要的
- 如果序列包是需要的就放入缓存中然后返回一个ACK,如果不是就丢弃
3.5 服务端根据请求信息处理
TCP头里面还有端口号,HTTP的服务器正在监听这个端口号。于是,目标机器自然知道是HTTP服务器这个进程想要这个包,于是将包发给HTTP服务器。HTTP服务器的进程看到原来这个请求是要访问一个网页,于是就把这个网页发给客户端
四、HTTP返回的构建
HTTP的返回报文也是有一定格式的,这也是基于HTTP 1.1
4.1 状态行
- 状态码
反应HTTP请求的结果。200意味着大吉大利,而404是服务端无法响应这个请求
- 短语
大概说一下原因
4.2 首部
接下来是返回首部的key value
4.2.1 Retry-After
Retry-After,表示告诉客户端应该在多长时间以后再次尝试一下。503错误是说服务暂时不再和这个值配合使用
4.2.2 Content-Type
在返回的头部里面也会有Content-Type,表示返回的是HTML,还是JSON
4.3 返回HTTP报文传输
4.3.1 TCP分小段并且保证可靠到达
构造好了返回的HTTP报文,接下来就是把这个报文发送出去。还是交给Socket去发送,还是 交给TCP层,让TCP层将返回的HTML,也分成一个个小的段,并且保证每个段都可靠到达
4.3.2 IP层封装MAC信息并路由到对应MAC层
这些段加上TCP头后会交给IP层,然后把刚才的发送过程反向走一遍。虽然两次不一定走相同 的路径,但是逻辑过程是一样的,一直到达客户端
4.3.3 客户端发送给对应端口的进程处理
客户端发现MAC地址符合、IP地址符合,于是就会交给TCP层。根据序列号看是不是自己要的报文段,如果是,则会根据TCP头中的端口号发给相应的进程。这个进程就是浏览器,浏览器作为客户端也在监听某个端口
4.3.4 浏览器根据HTML渲染出网页
当浏览器拿到了HTTP的报文,发现返回200则一切正常,于是就从正文中将HTML拿出来。HTML是一个标准的网页格式,浏览器只要根据这个格式就可以展示出一个绚丽多彩的网页
这就是一个正常的HTTP请求和返回的完整过程
五、HTTP 2.0
HTTP协议在不断地进化过程中,在HTTP1.1基础上便有了HTTP 2.0
5.1 HTTP 1.0存在的问题
HTTP 1.1在应用层以纯文本的形式进行通信,每次通信都要带完整的HTTP的头,而且不考虑 pipeline模式的话,每次的过程总是像上面描述的那样一去一回。这样在实时性、并发性上都 存在问题
5.2 HTTP 2.0相对于1.0的优化
5.2.1 HTTP 2.0在两端针对头信息建立索引表
为了解决这些问题,HTTP 2.0对HTTP的头进行一定的压缩,将原来每次都要携带的大量key value在两端建立一个索引表,对相同的头只发送索引表中的索引
5.2.2 HTTP 2.0将一个连接切分成多个流
另外,HTTP 2.0协议将一个TCP的连接中,切分成多个流,每个流都有自己的ID,而且流可以是客户端发往服务端,也可以是服务端发往客户端。它其实只是一个虚拟的通道,并且流是有优先级的
5.2.3 HTTP 2.0将传输信息分割成更小的消息和帧
HTTP 2.0还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。常见的帧有Header帧,用于传输Header内容,并且会开启一个新的流。再就是Data帧,用来传输正文实体,多个Data帧属于同一个流
5.3 HTTP 2.0数据流传输的方式
通过这两种机制,HTTP 2.0的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据
5.4 栗子
来举一个例子:
假设一个页面要发送三个独立的请求,一个获取css,一个获取js,一个获取图片jpg。如果使用HTTP 1.1就是串行的,但是如果使用HTTP 2.0就可以在一个连接里,客户端和服务端都可以同时发送多个请求或回应,而且不用按照顺序一对一对应
HTTP 2.0其实是将三个请求变成三个流,将数据分成帧,乱序发送到一个TCP连接中
5.5 HTTP 2.0的优势
HTTP 2.0的优势如下:
- 成功解决了HTTP 1.1的队首阻塞问题
- 不需要通过HTTP 1.x的pipeline机制用多条TCP连接来实现并行请求与响应,减少了TCP连接数对服务器性能的影响
- 将页面的多个数据css、js、 jpg等通过一个数据链接进行传输,能够加快页面组件的传输速度
六、QUIC协议的城会玩
HTTP 2.0虽然大大增加了并发性,但还是有问题的。因为HTTP 2.0也是基于TCP协议的,TCP协议在处理包时是有严格顺序的
当其中一个数据包遇到问题,TCP连接需要等待这个包完成重传之后才能继续进行。虽然HTTP 2.0通过多个stream,使得逻辑上一个TCP连接上的并行内容进行多路数据的传输, 然而这中间并没有关联的数据。一前一后,前面stream 2的帧没有收到,后面stream 1的帧也会因此阻塞
于是,就又到了从TCP切换到UDP,进行城会玩的时候了。这就是Google的QUIC协议,接下来看它是如何城会玩的
6.1 机制一:自定义连接机制
一条TCP连接是由四元组标识的,分别是源IP、源端口、目的IP、目的端口。 一旦一个元素发生变化时就需要断开重连,重新连接。在移动互联情况下,当手机信号不稳定或者在WIFI和移动网络切换时,都会导致重连,从而进行再次的三次握手,导致一定的时延
这在TCP是没有办法的,但是基于UDP就可以在QUIC自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个64位的随机数作为ID来标识,而且UDP是无连接的,所以当IP或者端口变化的时候,只要ID不变,就不需要重新建立连接
6.2 机制二:自定义重传机制
TCP为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题
任何一个序号的包发过去,都要在一定的时间内得到应答,否则一旦超时就会重发这个序号的包。那怎么样才算超时呢?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拼接,还是能够拼成一个流
6.3 机制三:无阻塞的多路复用
有了自定义的连接和重传机制,就可以解决上面HTTP 2.0的多路复用问题
同HTTP 2.0一样,同一条QUIC连接上可以创建多个stream来发送多个 HTTP 请求。但是,QUIC是基于UDP的,一个连接上的多个stream之间没有依赖。这样,假如stream2丢了一个UDP包,后面跟着stream3的一个UDP包,虽然stream2的那个包需要重传,但是stream3的包无需等待,就可以发给用户
6.4 机制四:自定义流量控制
TCP的流量控制是通过滑动窗口协议。QUIC的流量控制也是通过window_update
,来告诉对端它可以接受的字节数。但是QUIC的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个stream控制窗口
在TCP协议中,接收端的窗口的起始点是下一个要接收并且ACK的包,即便后来的包都到了放在缓存里面,窗口也不能右移,因为TCP的ACK机制是基于序列号的累计应答,一旦ACK了一个系列号就说明前面的都到了,所以只要前面的没到,后面的到了也不能ACK,就会导致后面的到了也有可能超时重传,浪费带宽
QUIC的ACK是基于offset的,每个offset的包来了,进了缓存就可以应答,应答后就不会重 发,中间的空挡会等待到来或者重发即可,而窗口的起始位置为当前收到的最大offset,从这 个offset到当前的stream所能容纳的最大缓存,是真正的窗口大小。显然,这样更加准确
另外,还有整个连接的窗口,需要对于所有的stream的窗口做一个统计
七、小结
针对以上内容总结一下:
- HTTP协议虽然很常用,也很复杂,重点记住GET、POST、 PUT、DELETE这几个方法, 以及重要的首部字段
- HTTP 2.0通过头压缩、分帧、二进制编码、多路复用等技术提升性能
- QUIC协议通过基于UDP自定义的类似TCP的连接、重试、多路复用、流量控制技术,进一步提升性能
以上是关于网络协议趣谈HTTP协议内容和传输的主要内容,如果未能解决你的问题,请参考以下文章