葡萄全视频:https://segmentfault.com/a/11...http的定义是基于client/server的(C/S的架构模型)通过可靠的链路交换信息,是一种无状态的请求/响应协议。http的结构是目前我们使用最多的http1.x协议。header和body我们都很熟悉,那什么是startline呢?startline就是我们所说的request_line或者status_line,就是GET/HTTP/1.1或者HTTP/1.1200OK的字段。在描述http的各种工作方式之前,先来熟悉一下TCP/IP模型:http的开发:http1.0http1.0:http1.0默认是没有keep-alive的,会提前应用层请求数据下面的处理会到应用层。这里只讲传输层和应用层。在http1.0中,每次请求都会建立一个tcp连接(三次握手),然后再进行http请求,这样每次和Server交互都需要一个新的连接!我们每一个链接的请求链接如下图所示:想象一下,每次发起请求都要经过上面说的这么长的流程,会消耗多少资源。http1.1基于这个问题,我们的http迎来了http1.1版本。1.1版本相比1.0版本有哪些变化?HTTP添加主机字段。HTTP1.1引入了Chunkedtransfer-coding和rangerequests来实现断点续传(实际上HTTP报文头使用了chunkedtransfercoding来分块传输实体主体)。HTTP1.1流水线(pipelining)理论,客户端可以同时发送多个HTTP请求,而不是一个一个等待响应再请求。注意:这个流水线只限于理论场景,大部分桌面浏览器还是选择默认关闭HTTP流水线!因此,现在使用HTTP1.1协议的应用程序可能会打开多个TCP连接!Http1.1针对以上资源消耗问题给出了根本性的处理。默认长链接,什么意思?不是为每个http请求都建立一个http连接,而是只建立一个tcp连接来处理多个请求。当然,这里的每个请求都是串行的,也就是不需要建立tcp连接,但是还是要排队的。而这可能会造成队头阻塞(比如发送100个请求,第一个被阻塞,后面的99个请求都请求不到)。http1.1默认的工作模式如下图所示:大家想象一下这种模式,有什么缺点?可以优化吗?在此之上,我们提出了两个问题1.需要排队2.可能造成队头阻塞。对于第一个问题,http1.1已经给出了解决方案,即pipline,而第二个问题刚刚开始有了过渡性的解决方案,即spdy协议(谷歌推出的增强http的协议,其功能包括数据流Multiplexing,请求优先级,HTTP头压缩,有兴趣的可以研究),再到现在的http2.0。首先说一下什么是pipline。管道是一种实现多个http请求但可以写入同一个套接字而不等待响应的技术。只有http1.1规范支持http流水线,1.0不支持。这意味着什么?我们可以看到上图是在一个tcp的串口连接中处理的,那么开启pipline之后是这样的:可以看到发送http请求不再是先发送然后等待响应再发送下一个请求这样我们可以看到所有请求统一启动,但是有一个问题,HTTPPipelining实际上是将多个HTTP请求放到一个TCP连接中,一个一个发送,不需要等待服务器在发送过程中确认之前的请求。对请求的响应;但是,客户端仍然必须按照发送的顺序接收响应!这就导致了它虽然解决了排队问题,但是仅仅解决了单边排队问题。最后按照请求的先后顺序接受数据。为什么?因为他们不知道哪个是第一个,哪个是第二个。这样也会出现队头阻塞的问题。总结一下,在http1.0的时候,我们就是一条流水线,一件一件的完成任务。在http1.1中,我们worker的能力提高了,可以一次发出多个工作请求,但是我们还没有掌握技巧,所以还是要按照规定,在等待所有工作到达的时候,按顺序一一处理。http2.0接下来是我们的http2.0,看看它是如何解决前面的问题的。为了解决队头阻塞问题,在http2.0中,其实是使用了stream结构,给每个stream分配一个streamId来解决队头阻塞问题。那么http2到底是何方神圣呢?首先说到http2,就不得不提https。http2是基于https的协议。对于https,我找到了一篇写得很好的文章。Wireshark抓包了解HTTPS请求过程。文章开头我们比较了http1和http2的结构。看起来它们完全不同,但实际上并非如此。http2以帧为最小单位。看下图,我们会发现http2只是一层封装。其实本质还是headers和body,只是http2用了更高级的方式展示。http1.xvshttp2.0关于http2的优点,我们还得从http1的缺点说起,因为有比较才会有伤害。http1的连接数是有限制的。对于同一个域名,浏览器最多只能同时建立6~8个TCP连接(不同浏览器不同)。为了解决数量限制,出现了域名分片技术,其实就是资源域划分,将资源放在不同的域名下(比如二级子域名),这样就可以创建连接和请求对于不同的域名,以一种易于使用的方式,但是这种技术的滥用也会带来很多问题,比如每个TCP连接本身都需要经过DNS查询,三步握手,慢启动等,而且它还会占用额外的CPU和内存。服务器连接数过多也很容易造成网络拥塞、堵车等。那么,http2是怎么做到的呢?http2采用了多路复用技术。在TCP连接上,我们可以不断地向对方发送帧。每个帧的stream_id表示这个帧属于哪个流。然后,对方收到后,根据stream_id将每个stream的所有帧拼接成一整块数据。把HTTP/1.1的每一个请求都当做一个流,那么多个请求就变成了多个流,请求响应数据被分成多个帧,不同流中的帧以交错的方式相互发送,就是多个流中的HTTP/2多路复用。同时,我们知道http1的body的长度是由header带来的,所以如果以http2的形式传输肯定会出问题,所以http2把body放到了length域,并且每一个流有自己的长度。最后根据流的头部长度是否等于每个流的长度来决定是否打包。同时,这种分包组合也解决了线头堵塞的问题。那么问题又来了,如何确定没有丢包呢?同一个stream的顺序是不是乱了?此时tcp会保证数据包的顺序,保证数据包不会丢失。header内容很多,每次请求header变化不大,也没有相应的压缩和传输优化方案。这里http2使用了hpack算法来压缩header的长度。原理是维护一个静态索引表和一个动态索引表的索引空间。hpack的原理是匹配当前连接中存在的索引空间。如果一个key值已经存在,则使用对应的Index代替第一个entry,比如":method:GET"可以匹配静态索引中的索引2,传输时只需要传输一个包含2的字节即可;如果在索引空间中不存在,则用字符编码传输,字符编码可以是哈夫曼编码,然后根据情况判断是否需要存入动态索引表,这样就省了很多这种形式的空间。明文传输不安全。http1采用明文传输,不安全。然后http2使用二进制分帧层来解决这个问题。帧是数据传输的最小单位,由二进制传输代替原来的明文传输。原始消息被分成更小的数据帧。为了尽可能减少请求的数量,需要做合并文件、sprite图片、资源内联等优化工作,但这无疑造成了单次请求内容变大,延迟变大的问题更高,嵌入式资源不能有效使用缓存机制。针对这种情况,http2引入了服务器端推送,浏览器发送一个请求,服务器主动将与本次请求相关的资源推送给浏览器,这样浏览器就不需要再发起后续的请求了,主要是为了资源内联.优化。应用层的resetconnection,对于HTTP/1来说,就是通过在tcp报文段中设置resetflag来通知对端关闭连接。这样会直接断开连接,下次再次发送请求时必须重新建立连接。HTTP/2引入了RST_STREAM类型的帧,可以在不断开连接的情况下取消一个请求的流,性能更好。这就是我们要说的关于http的全部内容。如果想了解更多,读者可以自行使用wireshark抓包。推荐两个比较好的抓包工具:wireshark和charles。参考文章:http协议解析(二)HTTP2详解
