HTTP请求的构建
- 请求行
- 请求方法,如get post put delete
-
首部字段
-
key value,如Accept-Charset 表示客户端可以接受的字符集,防止传过来是另外的字符集,导致乱码出现。
Content-Type指正文格式,例如进行post请求,如果正文是json就应该将这个值设为json
-
HTTP请求的发送
面向链接的方式发送,通过stream二进制流的方式传送诶对方,到了tcp层,会把二进制流转化为一个个的报文发给服务器。
发送每个报文对需要对方回应ack,如果没有回应就一直发送,tcp每发送一个报文都需要加上源地址喝目标地址,放到ip头里面,交给ip层传输。
ip层查看目标地址和自己是否是同一个局域网,如果是就发送arp协议请求这个目标地址对应的mac地址,将源mac和目标mac放入mac头发送,如果不在同一个局域网,发送到网管,还需要发送arp协议,获取网关mac,将源mac和网关mac放入mac头发送。
网关收到包发现mac符合,取出目标ip,根据路由协议找到下一跳路由,获取下一跳路由mac,将包发给下一跳路由器。最终到达目标的局域网,这个时候最后一跳发现目标地址在自己的某一个出口的局域网,于是在整个局域网发送arp,得到目标mac地址,将包发出去。
目标及其发现mac地址符合,ip地址符合,解析tcp的头,查看序列号,如果正确就返回一个ack,不是就丢弃。tcp头里有端口号,http服务器正在监听端口号,于是目标机器知道是http服务器这个进程想要这个包,于是将包发送给http服务器。
HTTP返回的构建
状态码这是大家都很熟悉的如200,201,301,302,403,404,500
首部字段key value,如Retry-After,表示告诉客户端应该在多长时间再尝试,content-type 返回的是html还是json,然后后面的发送过程和请求时客户端发送的过程一致。这就是一个正常的http请求和返回的完整过程。
HTTP 2.0
HTTP 1.1 在应用层以纯文本的形式进行通信,每次通信都要带完整的http头,而且不考虑pipeline模式的话,每次过程在实时行和并发性都存在问题。
http2.0 对http的头进行压缩,将每次携带的大量key value在两端建立一个索引表,对相同的头只发送索引表中的索引。
http协议将一个tcp的连接中,切分为多个流,每个流都有自己的id,而且流可以是一个双向的虚拟通道,流也是有优先级别的。http中还有所有的传输消息分割为更小的信息和真,并对他们采用二进制格式编码。
通过这两种机制,http2.0的客户端可以讲多个请求分到不同的流中,将请求内容拆成帧,这些帧可以乱序传送,根据帧首部的流标识符重新组装,根据优先级决定处理哪个流的数据。
比如一个页面要发送三个独立的请求,分别获取css,js,jpg,如果使用http1.1就是串行的,如果使用2.0就可以在一个链接里,客户端和服务端可以同时发送多个请求或回应,而不是按照顺序一对一对应。
HTTP2.0解决了http1.1的队首阻塞问题,减少了tcp连接数对服务器性能的影响,同时加快了页面组件的传输速度。
QUCI协议
虽然2.0是可以解决1.1的高并发阻塞问题,但是2.0也是基于tcp洗衣的,当其中任何一个包遇到问题,都会阻塞住。
机制一:自定义连接机制
tcp连接是由四元组标识的,分别是源ip,源端口,目的ip,目的端口,任何一个元素发生变化时,就需要重新连接。再次进行三次握手,导致延时,在udp中,是以一个64位随机数作为id来标识,而且是无连接的,只要id不变就不需要重新建立连接。
机制二:自定义重传机制
quic有个序列号是递增的,任何一个序列号的包只发送一次,下次加一。例如,发送一个包,序号是100,发现没有返回,再次发送就是101,如果返回的ack是100,就是对第一个包的响应,返回ack101是第二个包的响应。quic还定义了一个offset概念,发送的数据在这个数据流离有个偏移量offset,可以通过offset查看数据发送到哪里,只要这个offset的包没有来就重发,如果来了就拼接。
机制三:无阻塞的多路复用
有了自定义连接和重传机制,就可以解决上面http2.0的多路复用问题。同一条QUIC连接上可以创建多个stream,发送多个http请求,但是quic是基于udp的,一个链接上多个stream之间没有依赖,这样,如果stream2丢了一个udp,后面跟着stream3的udp,虽然stream需要重传,但是stream3的包无需等待就可以发送。
机制四:自定义流量控制
tcp的流量控制是通过滑动窗口协议,quic通过window_update,来告诉对端它可以接受的字节数,但是quic的窗口是适应自己的多路复用机制的,不但在一个链接上控制窗口,还在一个连接中的每个stream控制窗口。
在tcp协议中,接收端的窗口的起点是下一个要接受并且ack的包,即便后来的包到了,放在缓存里,窗口也不能右移,只要前面的没到,后面的到了也不能ack,导致后面的到了,也有可能超时重传,浪费贷款。
quic的ack是基于offset的,每个offset包来了,进了缓存就可以应答,应答后不会重发,中间的空档会等待到来或者重发即可,而窗口的起始位置就是当前收到的最大offset,从这个offset到当前的stream所能容纳的最大缓存,就是真正窗口大小。