http1.0和http1.1的区别

关于HTTP1.0和1.1的差异我参考了网不少博客文章,还参考了HTTP1.1协议标准中文版,总结起来一共有如下五个方面的差异:
长链接
Host域
带宽优化
消息传递
缓存

1. 长链接

       长链接(HTTP persistent connection ,也有翻译为持久链接),指数据传输完成了保持TCP链接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据;相反的就是短链接。

      HTTP1.1支持长链接(PersistentConnection)和请求的流水线(Pipelining)处理,而且默认使用长链接,若是加入"Connection: close ",才关闭。
     HTTP 1.0默认使用短链接,规定浏览器与服务器只保持短暂的链接,浏览器的每次请求都须要与服务器创建一个TCP链接,服务器完成请求处理后当即断开TCP链接,服务器不跟踪 每一个客户也不记录过去的请求。要创建长链接,能够在请求消息中包含Connection: Keep-Alive头域,若是服务器愿意维持这条链接,在响应消息中也会包含一个Connection: Keep-Alive的头域。

注:
1.有部分古老的HTTP1.0 代理不理解Keep-alive,而致使长链接失效:客户端-->代理-->服务端,客户端带有Keep-alive,而代理不认识,因而将 报文原封不动转给了服务端,服务端响应了Keep-alive,也被代理转发给了客户端,因而保持了“客户端-->代理”链接和“代理--> 服务端”链接不关闭,可是,当客户端第发送第二次请求时,代理会认为当前链接不会有请求了,因而忽略了它,长链接失效。所以在实际使用中咱们须要控制代理可以处理长链接,如今不少代理服务器自己也会支持长链接了,如Nginx代理,代理服务器有长链接处理逻辑,服务端无需作patch处理。

2.在实际使用中,HTTP头部有了Keep-Alive这个值并不表明必定会使用长链接,客户端和服务器端均可以无视这个值,也就是不按标准来,能够本身实现。


在Keep-Alive模式下,客户端如何判断请求所获得的响应数据已经接收完成(或者说客户端如何知道服务器发送数据的长度是多少)?

1.任何不含有消息体的消息(如1XXX、20四、304等响应消息和任何头(HEAD,首部)请求的响应消息),老是由一个空行(CLRF)结束。

2.若是出现了Transfer-Encoding头字段 而且值为非“identity”,那么transfer-length由“chunked” 传输编码定义,除非消息因为关闭链接而终止,后面会详细说一下chunked传输编码。

3. 若是出现了Content-Length头字段,其值表示消息体(entity)长度的字节数大小,且其值必需为非负整数,客户端(服务器)能够根据这个值来判断数据是否接收完成。。但若是同时设置了 Transfer-Encoding头字段,那么将不能发送Content-Length头字段。而且若是同时收到了Transfer-Encoding 字段和Content-Length头字段,那么必须忽略Content-Length字段。能够把Transfer-Encoding和 Content-Length当作互斥的两种头。

4.若是消息采用的媒体类型(media type)为"multipart/byteranges",且传输长度未能以上述方式指明,那么这种自分割的媒体类型已经定义了如何肯定传输长度。对客 户端而言,发送这种格式前应该确认接收者有能力解析;对服务端而言,收到一个由HTTP 1.1客户端发来的含有Range头字段且指定了multiple byte-range 的消息,即说明该客户端有能力解析针对该格式的响应。 Range头可能被1.0的代理转发,它对“multipart/byteranges”一无所知。服务器必须按本节一、三、5条所述对消息进行分割。

5.靠服务端关闭链接来肯定。服务端发送完消息体后会关闭链接,可是有的时候也会由于意外缘由关闭链接,这个也要注意一下。

关于Transfer-Encoding传输编码

当客户端向服务器请求一个静态页面或者一张图片时,服务器能够很清楚的知道内容大小,而后经过Content-length消息首部字段告诉客户端须要接收多少数据。可是若是是动态页面等时,服务器是不可能预先知道内容大小,这时就可使用Transfer-Encoding:chunk模式来传输数据了。即若是要一边产生数据,一边发给客户端,服务器就须要使用"Transfer-Encoding: chunked"这样的方式来代替Content-Length。

chunked传输模式,是HTTP/1.1的一大特性。消息得以拆成小块,并逐块传输,相对减轻了消息发送者缓冲的压力、容许其发送的内容在发送的同时动态产生,甚至长度不限。

chunked的消息结构大体能够描述为:

chunk-1,chunk-2,chunk-3...final-chunk,trailer

其中每块Chunk分为头部 扩展字段(可忽略)和正文部分,头部内容指定正文的字符总数(十六进制的数字)和单位(通常不写,默认字节),正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。
在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些附加的Header信息(一般能够直接忽略)。
最后一个块(零长度)结束以后会再传递一个拖尾(trailer),它包含一个或多个头域,这些头域是发送方在传递完全部 块以后再计算出值的,如长度、md5等。发送方会在消息中包含一个Trailer头域告诉接收方这个拖尾的存在,Trailer后面会有一个 CRLF 做为 结束标记符。经过看下图会更直观些:
transfer-encoding的可选值有:chunked,identity两个,前者指把要发送传输的数据切割成一系列的块数据 传输,后者指传输时不作任何处理,按自身的本质数据形式传输。
举个例子,若是咱们要传输一本“红楼梦”小说到服务器,chunked方式就会先把这本小说分 成一章一章的,而后逐个章节上传,而identity方式则是从小说的第一个字按顺序传输到最后一个字结束。
chunked格式图解以下:

 24E5是指第一个块数据长度为24E5(16进制格式字符串表示),CRLF为换行控制符。紧接着是第一个块数据内容,其长度就是上面定义的24E5,以CRLF标志结束。3485是指第二块数据长度为3485,CRLF结束,而后后面是第二块的数据内容......,以这样的格式直到全部的块数据结束。最后以“0”CRLF结束,表示数据传输完成(这里对比rfc规范内容,省略了chunk-extension和trailer的东西,由于这并不重要)。

问题来了,那若是transfer-encoding的值是“identity”时该如何肯定消息体的长度?
       我和少远讨论过这个问题,也查了一些资料,可是都没有明确谈到这个问题,个人见解是:若是transfer-encoding 的值为“identity”,会一直接收,直到服务端关闭链接来计算接收的长度,即用第5种方法来决定长度。

HTTP 流水线技术 
HTTP 流水线技术(HTTP pipelining,也有翻译为管道化链接)是指,在一个TCP链接内,多个HTTP请求能够并行,客户端不用等待上一次请求结果返回,就能够发出下一次请求,但服务器端必须按照接收到客户端请求的前后顺序依次回送响应结果,以保证客户端能 够区分出每次请求的响应内容。使用这个技术必需要求客户端和服务器端都支持,目前有部分浏览器彻底支持,而服务端的支持仅须要:按HTTP请求顺序正确返回Response(也就是请求&响应采用FIFO模式),即只要服务器可以正确处理使用HTTP pipelinning的客户端请求,那么服务器就算是支持了HTTP 流水线技术。

可是在现实环境中,请求、响应链沿途很多的服务器和代理不支持流水线,会把后续的请求吃掉;此外,因为要保证响应返回的顺序,会形成队头阻塞 (Head-of-line blocking)的问题,即靠前的响应一旦阻塞,会耽误后续的响应发送。因为上述两个缘由致使HTTP流水线技术对性能的提高并不明显(这个问题会在HTTP2.0中解决)。并且,还由于队头阻塞的缘由,使用这个技术必须是幂等的HTTP方法,由于客户端没法得知当前已经处理到什么地步,重试后可能发生不可预测的结果。( 幂等HTTP方法是指屡次操做, 结果是同样的,好比POST方法就不是幂等的:一样的报文,第一次POST跟第二次POST在服务端的表现可能会不同。)

因此当代浏览器从未将流水线全面铺开,而是广泛把并发链接数从协 议规定的2提高到了6( HTTP1.1的RFC规定使用流水线技术一个用户最多两个链接 )

2.HOST域

          HTTP1.1在Request消息头里头多了一个Host域,并且是必传的,HTTP1.0则没有这个域。
       在HTTP1.0中认为每台服务器都绑定一个惟一的IP地址,所以,请求消息中的URL并无传递主机名(hostname)。但随着虚拟主机技术的发 展,在一台物理服务器上能够存在多个虚拟主机(Multi-homed Web Servers),而且它们共享一个IP地址。
        HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中若是没有Host头域会报告一个错误(400 Bad Request)。此外,服务器应该接受以绝对路径标记的资源请求。
       
3.带宽优化
        HTTP/1.0中,存在一些浪费带宽的现象,例如客户端只是须要某个对象的一部分,而服务器却将整个对象送过来了。又好比下载大文件时不支持断点续传功能,在发生断连后不得不从新下载完整的包。

        HTTP/1.1中在请求消息中引入了range头域,它支持只请求资源的某个部分。在响应消息中Content-Range头域声明了返回的这部分对象的偏移值和长度。若是服务器相应地返回了对象所请求范围的内容,则响应码为206(Partial Content),它能够防止Cache将响应误觉得是完整的一个对象。

Range头域能够请求实体的一个或者多个子范围。例如
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节之后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999

Content-Range: 表示WEB服务器传送的范围,描述响应覆盖的范围和整个实体长度。 通常格式为:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth
例 如,传送头500个字节次字段的形式:Content-Range:bytes0- 499/1234 ,1234为整个实体的长度。

       另一种浪费带宽的状况是请求消息中若是包含比较大的实体内容,但不肯定服务器是否可以接收该请求(如是否有权限),此时若贸然发出带实体的请求,若是被拒绝也会浪费带宽。
     HTTP/1.1加入了一个新的状态码100(Continue)。客户端事先发送一个只带头域的请求,若是服务器由于权限拒绝了请求,就回送响应码401(Unauthorized);若是服务器接收此请求就回送响应码100,客户端就能够继续发送带实体的完整请求了。具体用法为:客户端在Request头部中包含Expect: 100-continue
Server看到以后呢若是回100 (Continue) 这个状态代码,客户端就继续发requestbody。(注意,HTTP/1.0的客户端不支持100响应码,这个是HTTP1.1才有的。)若是回401,客户端就知道是什么意思了。

4. Request method&&Status code

     HTTP1.1增长了OPTIONS,PUT, DELETE, TRACE, CONNECT这些Request方法
 HTTP1.1 增长的新的status code有:
(HTTP1.0没有定义任何具体的1xx status code, HTTP1.1有2个)
100 Continue
101 Switching Protocols
 
203 Non-Authoritative Information
205 Reset Content
206 Partial Content
 
302 Found ( 在HTTP1.0中有个 302 Moved Temporarily )
303 See Other
305 Use Proxy
307 Temporary Redirect
 
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Timeout
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Request Entity Too Large
414 Request-URI Too Long
415 Unsupported Media Type
416 Requested Range Not Satisfiable
417 Expectation Failed
 
504 Gateway Timeout
505 HTTP Version Not Supported

5. Cache (缓存)
       在HTTP/1.0中,已经定义很多有关缓存的头域:
Expires:浏览器会在指定过时时间内使用本地缓存,指明应该在何时认为文档已通过期,从而再也不缓存它,时间为格林威治时间GMT。例如: Expires: Thu, 19 Nov 1981 08:52:00 GMT 

Last-Modified:请求对象最后一次的修改时间 用来判断缓存是否过时 一般由文件的时间信息产生    

Date:生成消息的具体时间和日期,即当前的GMT时间。例如: Date: Sun, 17 Mar 2013 08:12:54 GMT

If-Modified-Since:客户端存取的该资源最后一次修改的时间,用来和服务器端的Last-Modified作比较


Set-Cookie: 用于把cookie 发送到客户端。例如: Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
 
Pragma:no-cache:客户端使用该头域说明请求资源不能从cache中获取,而必须回源获取。


HTTP/1.1在1.0的基础上加入了一些cache的新特性,
1. 当缓存对象的Age超过Expire时变为stale对象,cache不须要直接抛弃stale对象,而是与源服务器进行从新激活(revalidation)。

2. 为了使caching机制更加灵活,HTTP/1.1增长了Cache-Control头域(请求消息和响应消息均可使用),它支持一个可扩展的指 令子集。 请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if- cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must- revalidate、proxy-revalidate、max-age。各个消息中的指令含义以下: 

  Public指示响应可被任何缓存区缓存,而且在多用户间共享。
  Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理,此响应消息对于其余用户的请求无效。
  no-cache指示请求或响应消息不能缓存
  no-store用于防止重要的信息被无心的发布,在请求消息中发送将使得请求和响应消息都不使用缓存。
  max-age指示客户机能够接收生存期不大于指定时间(以秒为单位)的响应。   
  min-fresh指示客户机能够接收响应时间小于当前时间加上指定时间的响应。
  max-stale指示客户机能够接收超出超时期间的响应消息。
  must-revalidate:若是数据是过时的则去服务器进行获取

并且在请求消息或响应消息中设置Cache-Control并不会修改另外一个消息处理过程当中的缓存 处理过程。

3. Cache使用关键字索引在磁盘中缓存的对象,在HTTP/1.0中使用资源的URL做为关键字。但可能存在不一样的资源基于同一个URL的状况,要区别它们还须要客户端提供更多的信息,如Accept-Language和Accept-Charset头域。为了更好的支持这种内容协商机制(content negotiation mechanism),HTTP/1.1在响应消息中引入了Vary头域,该头域列出了请求消息中须要包含哪些头域用于内容协商。例如: Vary: Accept-Encoding
  
内容协商与vary的一点小研究
要了解 Vary 的做用,先得了解 HTTP 的内容协商机制。有时候,同一个 URL 能够提供多份不一样的文档,这就要求服务端和客户端之间有一个选择最合适版本的机制,这就是内容协商。
协商方式有两种,一种是服务端把文档可用版本列表发给客户端让用户选,这可使用 300 Multiple Choices 状态码来实现。这种方案有很多问题,首先多一次网络往返;其次服务端同一文档的某些版本多是为拥有某些技术特征的客户端准备的,而普通用户不必定了解这些细节。举个例子,服务端一般能够将静态资源输出为压缩和未压缩两个版本,压缩版显然是为支持压缩的客户端而准备的,但若是让普通用户选,极可能选择错误的版本。因此 HTTP 的内容协商一般使用另一种方案:服务端根据客户端发送的请求头中某些字段自动发送最合适的版本。能够用于这个机制的请求头字段又分两种:
内容协商专用字段(Accept 字段)、其余字段。
首先来看 Accept 字段,详见下表:
请求头字段                              说明                                  响应头字段
Accept                        告知服务器发送何种媒体类型     Content-Type
Accept-Language     告知服务器发送何种语言           Content-Language
Accept-Charset         告知服务器发送何种字符集       Content-Type
Accept-Encoding      告知服务器采用何种压缩方式   Content-Encoding

有时候,上面四个 Accept 字段并不够用,例如要针对特定浏览器如 IE6 输出不同的内容,就须要用到请求头中的 User-Agent 字段。相似的,请求头中的 Cookie 也可能被服务端用作输出差别化内容的依据。 因为客户端和服务端之间可能存在一个或多个中间实体(如缓存服务器),而缓存服务最基本的要求是给用户返回正确的文档。若是服务端根据不一样 User-Agent 返回不一样内容,而缓存服务器把 IE6 用户的响应缓存下来,并返回给使用其余浏览器的用户,确定会出问题 。因此 HTTP 协议规定,若是服务端提供的内容取决于 User-Agent 这样常规 Accept 协商字段以外的请求头字段,那么响应头中必须包含 Vary 字段,且 Vary 的内容必须包含 User-Agent。同理,若是服务端同时使用请求头中 User-Agent 和 Cookie 这两个字段来生成内容,那么响应中的 Vary 字段看上去应该是这样的:
Vary: User-Agent, Cookie
也就是说 Vary 字段用于列出一个响应字段列表,告诉缓存服务器遇到同一个 URL 对应着不一样版本文档的状况时,如何缓存和筛选合适的版本。



HTTP1.1中文文档:www.blogjava.net/sunchaojin/archive/2009/05/31/279164.html
    









































































一个 常见BUG 的缓存服务问题
按 照上面的说明,Accept-Encoding 属于内容协商专用字段,服务端只须要在响应头中增长 Content-Encoding 字段,用来指明内容压缩格式;或者不输出 Content-Encoding 代表内容未通过压缩就能够了。而缓存服务器,应该针对不一样的 Content-Encoding 缓存不一样内容,再根据具体请求中的 Accept-Encoding 字段返回最合适的版本。
可是有些实现得有 BUG 的缓存服务器,会忽略响应头中的 Content-Encoding,从而可能给不支持压缩的客户端返回缓存的压缩版本。有两个方案能够避免这种状况发生:
将响应头中的 Cache-Control 字段设为 private,告诉中间实体不要缓存它;
增长 Vary: Accept-Encoding 响应头,明确告知缓存服务器按照 Accept-Encoding 字段的内容,分别缓存不一样的版本;
一般为了更好的利用中间实体的缓存功能,咱们都用第二种方案。