Internet上的WWW服务与HTTP协议(非常非常不错的文档,推荐订阅)
Internet上的WWW服务与HTTP协议
兼容性----H1TP/1.1与HTTP/1.0后向兼容;运行1.1版本的web服务器可以与运行1.0版本的浏览器“对话”,运行1.1版本的浏览器也可以与运行1.0版本的Web服务器“对话”。(主要看客户端的浏览器支持的http协议版本和服务器的http协议版本)
可靠性传输----HTTP/1.0和HTTP/1.1都把TCP作为底层的传输协议,TCP给HTTP提供一个可靠的数据传输服务;这意味着由客户发出的每个HTTP请求消息最终将无损地到达服务器,由服务器发出的每个HTTP响应消息最终也将无损地到达客户.
拥塞控制机制----TCP还使用一个拥塞控制机制。该机制迫使每个新的TCP连接一开始以相对缓慢的速率传输数据,然而只要网络不拥塞,每个连接可以迅速上升到相对较高的速率。这个慢速传输的初始阶段称为缓启动(slow start)。
无状态的协议----在向客户发送所请求文件的同时,服务器并没有存储关于该客户的任何状态信息。即便某个客户在几秒钟内再次请求同一个对象,服务器也不会响应说:自己刚刚给它发送了这个对象。相反,服务器重新发送这个对象,因为它已经彻底忘记早先做过什么。既然HTTP服务器不维护客户的状态信息,我们于是说HTTP是一个无状态的协议(stateless protocol)。
非持久连接和持久连接:HTTP既可以使用非持久连接(nonpersistent connection),也可以使用持久连接(persistent connection)。HTTP/1.0使用非持久连接,HTTP/1.1默认使用持久连接。
非持久连接----
让我们查看一下非持久连接情况下从服务器到客户传送一个Web页面的步骤。假设该贝面由1个基本HTML文件和10个JPEG图像构成,而且所有这些对象都存放在同一台服务器主机中。 再假设该基本HTML文件的URL为:www.cnpaf.net/index.html。
下面是具体步骡:
1.HTTP客户初始化一个与服务器主机www.cnpaf.net中的HTTP服务器的TCP连接。HTTP服务器使用默认端口号80监听来自HTTP客户的连接建立请求。
2.HTTP客户经由与TCP连接相关联的本地套接字发出—个HTTP请求消息。这个消息中包含路径名/somepath/index.html。
3.HTTP服务器经由与TCP连接相关联的本地套接字接收这个请求消息,再从服务器主机的内存或硬盘中取出对象/somepath/index.html,经由同一个套接字发出包含该对象的响应消息。
4.HTTP服务器告知TCP关闭这个TCP连接(不过TCP要到客户收到刚才这个响应消息之后才会真正终止这个连接)。
5.HTTP客户经由同一个套接字接收这个响应消息。TCP连接随后终止。该消息标明所封装的对象是一个HTML文件。客户从中取出这个文件,加以分析后发现其中有10个JPEG对象的引用。
6.给每一个引用到的JPEG对象重复步骡1-4(即浏览器页面收到页面内容时候,会再去请求引用的资源,如图片、js等等)。
浏览器在接收web页面的同时把它显示给用户。不同的浏览器可能会以略有不同的方式解释(也就是向用户显示)同一个web页面。HTTP与客户如何解释Web页面没有任何关系,其规范([RFC 1945]和[RFC 2616I)仅仅定义HTTP客户程序和服务器程序之间的通信协议。
上述步骤之所以称为使用非持久连接,原因是每次服务器发出一个对象后,相应的TCP连接就被关闭(包括页面引用的对象资源请求),也就是说每个连接都没有持续到可用于传送其他对象。每个TCP连接只用于传输一个请求消息和一个响应消息。就上述例子而言,用户每请求一次那个web页面,就产生11个TCP连接。 |
在上述步骡中,我们有意不说清客户是通过10个串行的TCP连接先后取得所有JPEG对象,还是通过并行的TCP连接同时取得其中某些JPEG对象。实际上,现今的浏览器允许用户通过配置来控制并行连接的程度。大多数浏览器默认可以打开5到10个并行的TCP连接,每个连接处理一个请求—响应事务。用户要是喜欢,可以把最大并行连接数设为l,那样的话这10个连接是串行地建立的。我们将在第3章看到,使用并行连接可以缩短响应时间。
用户的点击导致浏览器发起建立一个与Web服务器的TCP连接;这里涉及·—次“三次握手”过程——首先是客户向服务器发送一个小的冗余消息,接着是服务器向客户确认并响应以一个小的TCP消息,最后是客户向服务器回确认。每个请求(资源)总的响应时间粗略地算是2个RTT(往返时间)加上服务器发送这个HTMI文件的时间(包含tcp的3次握手):一个RTT用于建立TCP连接,另—个RTT用于请求和接收对象
非持久连接有些缺点。首先,客户得为每个待请求的对象建立并维护一个新的连接。对于每个这样的连接,TCP得在客户端和服务器端分配TCP缓冲区,并维持TCP变量。对于有可能同时为来自数百个不同客户的请求提供服务的web服务器来说,这会严重增加其负担。其次,如前所述,每个对象都有2个RTT的响应延长——一个RTT用于建立TCP连接,另—个RTT用于请求和接收对象。最后,每个对象都遭受TCP缓启动,因为每个TCP连接都起始于缓启动阶段。不过并行TCP连接的使用能够部分减轻RTT延迟和缓启动延迟的影响。 |
持久连接---
在持久连接情况下,服务器在发出响应后让TCP连接继续打开着。同一对客户/服务器之间的后续请求和响应可以通过这个连接发送。整个Web页面(上例中为包含一个基本HTMLL文件和10个图像的页面)自不用说可以通过单个持久TCP连接发送:甚至存放在同一个服务器中的多个web页面也可以通过单个持久TCP连接发送。通常,HTTP服务器在某个连接闲置一段特定时间后关闭它,而这段时间通常是可以配置的;
持久连接分为不带流水线(without pipelining)和带流水线(with pipelining)两个版本。如果是不带流水线的版本,那么客户只在收到前一个请求的响应后才发出新的请求。这种情况下,web页面所引用的每个对象(上例中的10个图像)都经历1个RTT的延迟,用于请求和接收该对象。与非持久连接2个RTT的延迟相比,不带流水线的持久连接已有所改善,不过带流水线的持久连接还能进一步降低响应延迟。不带流水线版本的另一个缺点是,服务器送出一个对象后开始等待下一个请求,而这个新请求却不能马上到达。这段时间服务器资源便闲置了。
HTTP/1.1的默认模式使用带流水线的持久连接。这种情况下,HTTP客户每碰到一个引用就立即发出一个请求,因而HTTP客户可以一个接一个紧挨着发出各个引用对象的请求。服务器收到这些请求后,也可以一个接一个紧挨着发出各个对象。如果所有的请求和响应都是紧挨着发送的,那么所有引用到的对象一共只经历1个RTT的延迟(而不是像不带流水线的版本那样,每个引用到的对象都各有1个RTT的延迟)。另外,带流水线的持久连接中服务器空等请求的时间比较少。与非持久连接相比,持久连接(不论是否带流水线)除降低了1个RTT的响应延迟外,缓启动延迟也比较小。其原因在于既然各个对象使用同一个TCP连接,服务器发出第一个对象后就不必再以一开始的缓慢速率发送后续对象。相反,服务器可以按照第一个对象发送完毕时的速率开始发送下一个对象。
HTTP消息格式
HTTP规范1.0[RPcl945]和1.1[RFC 2616]定义了HTTP消息的格式。HTTP消息分为请求消息和响应稍息两类。下面我们分别进行介绍。
HTTP请求消息
下面是一个典型的HTTP请求消息:
GET /somedir/page.html H7TP/1.1
Host:www.cnpaf.net
Connection:close
User-agent:Mozilla/4.0
Accept-language:zh-cn
(额外的回车符和换行符)
仔细检查这个简单的请求消息,我们可从中学到不少东西。首先,这个消息是用普通的ASCII文本书写的。其次,这个消息共有5行(每行以一个回车符和一个换行符结束),最后一行后面还有额外的一个回车特和换行符。当然,一个请求消息可以不止这么多行,也可以仅仅只有一行。该请求消息的第一行称为请求行(request line),后续各行都称为头部行(header)。请求行有3个宁段:方法字段、URL字段、HTTP版本宇段。方法字段有若干个值可供选择,包括GET、POST和HEAD。HTTP请求消息绝大多数使用GET方法,这是浏览器用来请求对象的方法,所请求的对象就在URL字段中标识。本例表明浏览器在请求对象/somedir/page.html。版本字段是不言自明的;本例中浏览器实现的是HTTP/1.1版本。
HTTP响头部行Host:www.cnpaf.net定存放所请求对象的主机。请求消息中包含头部Connection:close是在告知服务器本浏览器不想使用持久连接;服务器发出所请求的对象后应关闭连接。尽管产生这个请求消息的浏览器实现的是HTTP/1.1版本,它还是不想使用持久连接。User-agent头部行指定用户代理,也就是产生当前请求的浏览器的类型。本例的用户代理是Mozilla/4.0,它是Nelscape浏览器的一个版本。这个头部行很有用,因为服务器实际上可以给不同类型的用户代理发送同一个对象的不同版本(这些不同版本位用同一个URL寻址)。最后,Accept-languag:头部行指出要是所请求对象有简体中文版本,那么用户宁愿接收这个版本;如果没有这个语言版本,那么服务器应该发送其默认版本。Accept-languag:仅仅是HTTP的众多内容协商头部之一。HEAD方法通常用于HTTP服务器软件开发人员进行调试。
应消息
下面是一个典型的HTTP响应消息:
HTTP/1.1 200 0K
Connectlon:close
Date: Thu, 13 Oct 2005 03:17:33 GMT
Server: Apache/2.0.54 (Unix)
Last—Nodified:Mon,22 Jun 1998 09;23;24 GMT
Content—Length:682l
Content—Type:text/html
(数据 数据 数据 数据 数据…………)
这个响应消息分为3部分:1个起始的状态行(status line),6个头部行、1个包含所请求对象本身的附属体。状态行有3个字段:协议版本字段、状态码字段、原因短语字段。本例的状态行表明,服务器使用HTTP/1.1版本,响应过程完全正常(也就是说服务器找到了所请求的对象,并正在发送)。
现在看一下本例中的各个头部行。服务器使用Connectlon:close头部行告知客户自己将在发送完本消息后关闭TCP连接。Date:头部行指出服务器创建并发送本响应消息的日期和时间。注意,这并不是对象本身的创建时间或最后修改时间,而是服务器把该对象从其文件系统中取出,插入响应消息中发送出去的时间。Server:头部行指出本消息是由Apache服务器产生的;它与HTTP请求消息中的User-agent:头部行类似。Last—Nodified:头部行指出对象本身的创建或最后修改日期或时间。Last—Nodified:头部对于对象的高速缓存至关重要,且不论这种高速缓存是发生在本地客户主机上还是发生在网络高速缓存服务器主机(也就是代理服务器主机)上。Content—Length:头部行指出所发送对象的字节数。Content—Type:头部行指出包含在附属体中的对象是HTML文本。对象的类型是由Content—Type:头部而不是由文件扩展名正式指出的。
注意,如果服务器收到一个HTTP/1.0的请求,那么它即使是一个HTTP/1.1服务器,也不会使用持久连接。相反,这样的HTTP/1.1服务器会在发出所请求的对象后关闭TCP连接。这么做是必要的,因为HTTP/1.0客户期待服务器马上关闭连接。
我们接着看一下如下图所示的响应消息的一般格式。前面的响应消息例子完全符合这个格式。响应消息中的状态码和原因短语指示相应请求的处理结果
●200 0K;请求成功,所请求信息在响应消息中返回。
●301 Moved Permanently:所请求的对象己永久性迁移;新的URL在本响应消息的Location:头部指出。客户软件会自动请求这个新的URL。
●400 Bad Request;表示服务器无法理解相应请求的普通错误的状态码
●404 Not Found:服务器上不存在所请求的文档。
●HTTP Version Not Support:服务器不支持所请求的HTTP协议版本。
身份认证和cookie(用户—服务器交互)
我们已经知道HTTP服务器是无状态的。这样的处理可以简化服务器程序的设计,以便开发出更高性能的Web服务器软件。然而,一个Web站点往往有标识其用户的需求,因为其web服务器可能希望限制用户的访问,也可能想要根据用户的身份来提供内容。HTTP提供了两种帮助服务器标识用户的机制:身份认证和cookie。
身份认证许:多web站点要求用户提供一个用户名—口令对才能访问存放在其服务器中的文档。这种要求称为身份认证(authentication)。HTTP提供特殊的状态码和头部来帮助Web站点执行身份认证。我们通过查看一个例子来领会这些特殊的状态码和头部如何工作。假设有—个客户在请求来自某个服务器的一个对象,而该服务器要求用户授予权限。
客户首先发送一个不合特殊头部的普通请求消息。服务器以空的附属体和一个“401Authorization Required”状态码作为响应。服务器还在这个响应消息中包含“个WWW-Authenticate:头部,说明具体如何执行身份认证。这个头部的典型值是指出用户需要提供一个用户名—口令对。
客户收到这个响应消息后提示用户输入用户名和口令,然后重新发送请求消息。这一回客户在请求消息中包含了一个Authorization:头部,其中包含有用户输入的用户名和口令。
取得第一个对象后,客户在同为请求该服务器上对象的后续请求中继续发送这个用户名—口令对。这个做法一般将持续到用户关闭浏览器为止。在浏览器未被关闭之前,这个用户名—口令对是高速缓存着的,因此浏览器不会每请求一个对象就提示用户输入一次用户名和口令。通过上述方式,要求用户授权的Web站点就能标识出每个请求的用户了。
cookie是一种可让Web站点用来跟踪用户的候选机制,定义在RFC 2109中。有些Web站点使用cookie,其他Web站点则不用。下面查看一个例子。假设一个客户首次联系一个使用cookie的web站点。服务器会在其响应中包含一个Set—Cookie:头部。该头部的值可以是一个由Web服务器产生的客户标识数.例如:
Set-Cookie:1678453
客户收到这个响应消息,看到其中的Set-Cookie:头部和标识数后,会在存放在客户主机中的某个特殊的cookie文件中添加一行。这一行一般包含服务器主机的主机名和这个与用户关联的标识数。在一段时间(如一个星期)之后请求同一个服务器时,由同一个用户启动的新客户会在请求消息中包含一个cookie头部,其值为早先由该服务器产生的标识数,例如:Cookie:1678453
在这种方式中,服务器并不知道提出请求的用户的用户名,但是它确实知道该用户与一个星期前提出请求的用户是同一个。
Web服务器有多个使用cookie的目的:
●如果服务器要求身份认证,但又不想在同一用户每次访问本Web站点时都麻烦他输入用户名和口令,那么可以设置一个cookie。
●如果服务器想要记住用户的偏好,以便在他们后续访问期间有目的地提供广告,那么可以设置一个cookie。
●如果web站点提供购物服务,那么服务器可以使用cookie跟踪用户购买的物品,就是建立一个虚拟的购物车。
需指出的是,cookie不适用于会从不同主机访问同一web站点的游动用户。这种情况下,该web站点会把同一个用户在不同主机上的使用看成是由新的用户执行的。(窃取用户cookie可以)
带条件的GET
Web高速缓存技术通过就近存取先前取得的对象来降低对象检索延迟,减少因特网上的web流量。Web的高速缓存既可以驻留在客户主机中,也可以驻留在中间网络高速缓存服务器主机中。我们将在稍后讨论网络高速缓存,这里只关注客户的高速缓存。
Web高速缓存在降低用户可感知的响应时间的同时,却引入了一个新的问题——高速缓存中存放的对象的拷贝可能是过期的。换句话说,存放在web服务器中的对象可能己在客户高速缓存下它的一个拷贝之后被修改了。幸运的是,HTTP提供一个专门的机制,使得在允许客户进行高速缓存的同时,仍确保传递给浏览器的所有对象都是最新的。这个机制称为带条件的0ET(conditional GET)。满足条件(1)使用GET方法和(2)包含If-Modified-S1nce:头部的HTTP请求消息就是所谓的带条件的Get消息。
我们通过查看一个例子来说明带条件的GET如何工作,向服务器请求一个尚未高速缓存的对象:
GET /fruit/kiwi.gif HTTP/1.0
User—agent: Mozilla/4.0
接着,web服务器把带这个对象的一个响应消息发送给客户:
HTTP/1.0 200 OK
Date: Thu, 13 Oct 2005 05:33:47 GMT
Server: Apache/2.0.54 (Unix)
Last-Modified:Thu, 13 Oct 2005 02:32:47 GMT
Content-Type:image/gif
(数据 数据 数据 数据 数据……)
客户把这个对象显示给用户,同时把它保存在自己的本地高速缓存中客户还随该对象本身高速缓存最后修改日期与时间。一个星期之后,同一个用户请求同一个对象,而该对象仍然存放在高速缓存中。既然web服务器中的该对象有可能已在最近一个星期被修改过,于是浏览器发出一个带条件的GET消息,执行判定高速缓存的对象拷贝是否为最新的检查;
GET /fruit/kiwi.gif HTTP/1.0
User—agent: Mozilla/4.0
If—Modlfied—Since:Thu, 13 Oct 2005 02:32:47 GMT
其中,If—Modlfied—Since:头部的值就等于一个星期前由服务器发送的Last-Modified:头部的值。这个带条件的GET消息告知服务器,只有在该对象自所指定的时间以来被修改了的前提下才发送它。假设该对象在这段时间内未曾被修改过,那么服务器将发送一个附属体为空的响应消息给客户;
HTTP/1.0 304 Not Modified
Date: Thu, 20 Oct 2005 05:33:47 GMT
Server: Apache/2.0.54 (Unix)
我们看到,web服务器仍然发送——个响应消息作为带条件的GET消息的响应,不过其中不包含所请求的对象。包含该对象只会浪费带宽,并延长用户可感知的响应时间,特别是在该对象很大的时候。注意,这个响应消息的状态为“304 Not Modified”,它告知客户可以放心使用所请求对象的高速缓存版本。