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、204、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”一无所知。服务器必须按本节1、3、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 字段的内容,分别缓存不同的版本;
通常为了更好的利用中间实体的缓存功能,我们都用第二种方案。



    
    


   

你可能感兴趣的:(网络专栏)