HTTP协议

来源:互联网 发布:Servlet接收json gson 编辑:程序博客网 时间:2024/06/05 11:30

HTTP协议漫谈

简介

HTTP的定义和历史

    在一个网络中。传输数据需要面临三个问题:

    1.客户端如何知道所求内容的位置?

    2.当客户端知道所求内容的位置后,如何获取所求内容?

    3.所求内容以何种形式组织以便被客户端所识别?

 

     对于WEB来说,回答上面三种问题分别采用三种不同的技术,分别为:统一资源定位符(URIs),超文本传输协议(HTTP)和超文本标记语言(HTML)。对于大多数WEB开发人员来说URI和HTML都是非常的熟悉。而HTTP协议在很多WEB技术中都被封装的过多使得HTTP反而最不被熟悉。

    HTTP作为一种传输协议,也是像HTML一样随着时间不断演进的,目前流行的HTTP1.1是HTTP协议的第三个版本。

 

HTTP 0.9

    HTTP 0.9作为HTTP协议的第一个版本。是非常弱的。请求(Request)只有一行,比如:

GET www.cnblogs.com

    从如此简单的请求体,没有POST方法,没有HTTP 头可以看出,那个时代的HTTP客户端只能接收一种类型:纯文本。并且,如果得不到所求的信息,也没有404 500等错误出现。

    虽然HTTP 0.9看起来如此弱,但已经能满足那个时代的需求了。

 

HTTP 1.0

    随着1996年后,WEB程序的需求,HTTP 0.9已经不能满足需求。HTTP1.0最大的改变是引入了POST方法,使得客户端通过HTML表单向服务器发送数据成为可能,这也是WEB应用程序的一个基础。另一个巨大的改变是引入了HTTP头,使得HTTP不仅能返回错误代码,并且HTTP协议所传输的内容不仅限于纯文本,还可以是图片,动画等一系列格式。

    除此之外,还允许保持连接,既一次TCP连接后,可以多次通信,虽然HTTP1.0 默认是传输一次数据后就关闭。

 

HTTP 1.1

    2000年5月,HTTP1.1确立。HTTP1.1并不像HTTP1.0对于HTTP0.9那样的革命性。但是也有很多增强。

    首先,增加了Host头,比如访问我的博客: 

 GET /Careyson HTTP/1.1  Host: www.cnblogs.com 

 

    Get后面仅仅需要相对路径即可。这看起来虽然仅仅类似语法糖的感觉,但实际上,这个提升使得在Web上的一台主机可以存在多个域。否则多个域名指向同一个IP会产生混淆。

    此外,还引入了Range头,使得客户端通过HTTP下载时只下载内容的一部分,这使得多线程下载也成为可能。

    还有值得一提的是HTTP1.1 默认连接是一直保持的,这个概念我会在下文中具体阐述。

 

HTTP的网络层次

    在Internet中所有的传输都是通过TCP/IP进行的。HTTP协议作为TCP/IP模型中应用层的协议也不例外。HTTP在网络中的层次如图1所示。

    1

     图1.HTTP在TCP/IP中的层次

 

    可以看出,HTTP是基于传输层的TCP协议,而TCP是一个端到端的面向连接的协议。所谓的端到端可以理解为进程到进程之间的通信。所以HTTP在开始传输之前,首先需要建立TCP连接,而TCP连接的过程需要所谓的“三次握手”。概念如图2所示。

    22

    图2.TCP连接的三次握手

 

   在TCP三次握手之后,建立了TCP连接,此时HTTP就可以进行传输了。一个重要的概念是面向连接,既HTTP在传输完成之间并不断开TCP连接。在HTTP1.1中(通过Connection头设置)这是默认行为。所谓的HTTP传输完成我们通过一个具体的例子来看。

    比如访问我的博客,使用Fiddler来截取对应的请求和响应。如图3所示。

    3

图3.用fiddler抓取请求和相应

 

    可以看出,虽然仅仅访问了我的博客,但锁获取的不仅仅是一个HTML而已,而是浏览器对HTML解析的过程中,如果发现需要获取的内容,会再次发起HTTP请求去服务器获取,比如图2中的那个common2.css。这上面19个HTTP请求,只依靠一个TCP连接就够了,这就是所谓的持久连接。也是所谓的一次HTTP请求完成。

 

HTTP请求(HTTP Request)

    所谓的HTTP请求,也就是Web客户端向Web服务器发送信息,这个信息由如下三部分组成:

    1.请求行

    2.HTTP头

    3.内容

 

    一个典型的请求行比如:

GET www.cnblogs.com HTTP/1.1

    请求行写法是固定的,由三部分组成,第一部分是请求方法,第二部分是请求网址,第三部分是HTTP版本。

 

    第二部分HTTP头在HTTP请求可以是3种HTTP头:1.请求头(request header)  2.普通头(general header)  3.实体头(entity header)

    通常来说,由于Get请求往往不包含内容实体,因此也不会有实体头。

 

    第三部分内容只在POST请求中存在,因为GET请求并不包含任何实体。

    我们截取一个具体的Post请求来看这三部分,我在一个普通的aspx页面放一个BUTTON,当提交后会产生一个Post请求,如图4所示。

    4

    图4.HTTP请求由三部分组成

 

HTTP请求方法

    虽然我们所常见的只有Get和Post方法,但实际上HTTP请求方法还有很多,比如: PUT方法,DELETE方法,HEAD方法,CONNECT方法,TRACE方法。这里我就不细说了,自行Bing。

    这里重点说一下Get和Post方法,网上关于Get和Post的区别满天飞。但很多没有说到点子上。Get和Post最大的区别就是Post有上面所说的第三部分:内容。而Get不存在这个内容。因此就像Get和Post其名称所示那样,Get用于从服务器上取内容,虽然可以通过QueryString向服务器发信息,但这违背了Get的本意,QueryString中的信息在HTTP看来仅仅是获取所取得内容的一个参数而已。而Post是由客户端向服务器端发送内容的方式。因此具有请求的第三部分:内容。

 

HTTP响应(HTTP Response)

       当Web服务器收到HTTP请求后,会根据请求的信息做某些处理(这些处理可能仅仅是静态的返回页,或是包含Asp.net,PHP,Jsp等语言进行处理后返回),相应的返回一个HTTP响应。HTTP响应在结构上很类似于HTTP请求,也是由三部分组成,分别为:

    1.状态行

    2.HTTP头

    3.返回内容

 

    首先来看状态行,一个典型的HTTP状态如下:

HTTP/1.1 200 OK

    第一部分是HTTP版本,第二部分是响应状态码,第三部分是状态码的描述,因此也可以把第二和第三部分看成一个部分。

    对于HTTP版本没有什么好说的,而状态码值得说一下,网上对于每个具体的HTTP状态码所代表的含义都有解释,这里我说一下分类。

  • 信息类 (100-199)

  • 响应成功 (200-299)

  • 重定向类 (300-399)

  • 客户端错误类 (400-499)

  • 服务端错误类 (500-599)

 

    HTTP响应中包含的头包括1.响应头(response header) 2.普通头(general header) 3.实体头(entity header)。

    第三部分HTTP响应内容就是HTTP请求所请求的信息。这个信息可以是一个HTML,也可以是一个图片。比如我访问百度,HTTP Response如图5所示。

    5

    图5.一个典型的HTTP响应

 

   图5中的响应是一个HTML,当然还可以是其它类型,比如图片,如图6所示。

    6

    图6.HTTP响应内容是图片

 

    这里会有一个疑问,既然HTTP响应的内容不仅仅是HTML,还可以是其它类型,那么浏览器如何正确对接收到的信息进行处理?

    这是通过媒体类型确定的(Media Type),具体来说对应Content-Type这个HTTP头,比如图5中是text/html,图6是image/jpeg。

    媒体类型的格式为:大类/小类  比如图5中的html是小类,而text是大类。

    IANA(The Internet Assigned Numbers Authority,互联网数字分配机构)定义了8个大类的媒体类型,分别是:

  • application— (比如: application/vnd.ms-excel.)

  • audio (比如: audio/mpeg.)

  • image (比如: image/png.)

  • message (比如,:message/http.)

  • model(比如:model/vrml.)

  • multipart (比如:multipart/form-data.)

  • text(比如:text/html.)

  • video(比如:video/quicktime.)

 

HTTP头

    HTTP头仅仅是一个标签而已,比如我在Aspx中加入代码:

Response.AddHeader("测试头","测试值");

    对应的我们可以在fiddler抓到的信息如图7所示。

    7

    图7.HTTP头

   

    不难看出,HTTP头并不是严格要求的,仅仅是一个标签,如果浏览器可以解析就会按照某些标准(比如浏览器自身标准,W3C的标准)去解释这个头,否则不识别的头就会被浏览器无视。对服务器也是同理。假如你编写一个浏览器,你可以将上面的头解释成任何你想要的效果微笑

    下面我们说的HTTP头都是W3C标准的头,我不会对每个头的作用进行详细说明,关于HTTP头作用的文章在网上已经很多了,请自行Bing。HTTP头按照其不同的作用,可以分为四大类。

 

通用头(General header)

    通用头即可以包含在HTTP请求中,也可以包含在HTTP响应中。通用头的作用是描述HTTP协议本身。比如描述HTTP是否持久连接的Connection头,HTTP发送日期的Date头,描述HTTP所在TCP连接时间的Keep-Alive头,用于缓存控制的Cache-Control头等。

 

实体头(Entity header)

    实体头是那些描述HTTP信息的头。既可以出现在HTTP POST方法的请求中,也可以出现在HTTP响应中。比如图5和图6中的Content-Type和Content-length都是描述实体的类型和大小的头都属于实体头。其它还有用于描述实体的Content-Language,Content-MD5,Content-Encoding以及控制实体缓存的Expires和Last-Modifies头等。

 

请求头(HTTP Request Header)

    请求头是那些由客户端发往服务端以便帮助服务端更好的满足客户端请求的头。请求头只能出现在HTTP请求中。比如告诉服务器只接收某种响应内容的Accept头,发送Cookies的Cookie头,显示请求主机域的HOST头,用于缓存的If-Match,If-Match-Since,If-None-Match头,用于只取HTTP响应信息中部分信息的Range头,用于附属HTML相关请求引用的Referer头等。

响应头(HTTP Response Header)

    HTTP响应头是那些描述HTTP响应本身的头,这里面并不包含描述HTTP响应中第三部分也就是HTTP信息的头(这部分由实体头负责)。比如说定时刷新的Refresh头,当遇到503错误时自动重试的Retry-After头,显示服务器信息的Server头,设置COOKIE的Set-Cookie头,告诉客户端可以部分请求的Accept-Ranges头等。

 

状态保持

    还有一点值得注意的是,HTTP协议是无状态的,这意味着对于接收HTTP请求的服务器来说,并不知道每一次请求来自同一个客户端还是不同客户端,每一次请求对于服务器来说都是一样的。因此需要一些额外的手段来使得服务器在接收某个请求时知道这个请求来自于某个客户端。如图8所示。

    8

    图8.服务器并不知道请求1和请求2来自同一个客户端

 

 

通过Cookies保持状态

    为了解决这个问题,HTTP协议通过Cookies来保持状态,对于图8中的请求,如果使用Cookies进行状态控制,则变成了如图9所示。

    9

    图9.通过Cookies,服务器就可以清楚的知道请求2和请求1来自同一个客户端

 

通过表单变量保持状态

    除了Cookies之外,还可以使用表单变量来保持状态,比如Asp.net就通过一个叫ViewState的Input=“hidden”的框来保持状态,比如:

 <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0OTM4MTAwNGRkXUfhlDv1Cs7/qhBlyZROCzlvf5U=" />

    这个原理和Cookies大同小异,只是每次请求和响应所附带的信息变成了表单变量。

 

通过QueryString保持状态

     这个原理和上述两种状态保持方法原理是一样的,QueryString通过将信息保存在所请求地址的末尾来向服务器传送信息,通常和表单结合使用,一个典型的QueryString比如:

 www.xxx.com/xxx.aspx?var1=value&var2=value2

 

总结

    本文从一个比较高的视角来看HTTP协议,对于HTTP协议中的细节并没有深挖,但对于HTTP大框架有了比较系统的介绍,更多关于HTTP的细节信息,请去Bing或参看相关书籍:-)



常用的请求方式

常用的请求方式是GET和POST.

l         GET方式:是以实体的方式得到由请求URI所指定资源的信息,如果请求URI只是一个数据产生过程,那么最终要在响应实体中返回的是处理过程的结果所指向的资源,而不是处理过程的描述。

l         POST方式:用来向目的服务器发出请求,要求它接受被附在请求后的实体,并把它当作请求队列中请求URI所指定资源的附加新子项,Post被设计成用统一的方法实现下列功能:

1:对现有资源的解释;

2:向电子公告栏、新闻组、邮件列表或类似讨论组发信息;

3:提交数据块;

4:通过附加操作来扩展数据库 。

从上面描述可以看出,Get是向服务器发索取数据的一种请求;而Post是向服务器提交数据的一种请求,要提交的数据位于信息头后面的实体中。

GET与POST方法有以下区别:

(1)   在客户端,Get方式在通过URL提交数据,数据在URL中可以看到;POST方式,数据放置在HTML HEADER内提交

(2)   GET方式提交的数据最多只能有1024字节,而POST则没有此限制

(3)   安全性问题。正如在(1)中提到,使用 Get 的时候,参数会显示在地址栏上,而 Post 不会。所以,如果这些数据是中文数据而且是非敏感数据,那么使用 get;如果用户输入的数据不是中文字符而且包含敏感数据,那么还是使用 post为好。

(4)   安全的和幂等的。所谓安全的意味着该操作用于获取信息而非修改信息。幂等的意味着对同一 URL 的多个请求应该返回同样的结果。完整的定义并不像看起来那样严格。换句话说,GET 请求一般不应产生副作用。从根本上讲,其目标是当用户打开一个链接时,她可以确信从自身的角度来看没有改变资源。比如,新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。反之亦然。POST 请求就不那么轻松了。POST 表示可能改变服务器上的资源的请求。仍然以新闻站点为例,读者对文章的注解应该通过 POST 请求实现,因为在注解提交之后站点已经不同了(比方说文章下面出现一条注解)。

说 POST 比 GET 安全肯定是错的,POST跟GET都是明文传输,用httpfox等插件,或者像WireShark 等类似工具就能观察到。

POST和GET的差别其实是很大的。语义上,GET是获取指定URL上的资源,是读操作,重要的一点是不论对某个资源GET多少次,它的状态是不会改变的,在这个意义上,我们说GET是安全的(不是被密码学或者数据保护意义上的安全)。因为GET是安全的,所以GET返回的内容可以被浏览器,Cache服务器缓存起来(其中还有很多细节,但不影响这里的讨论)。

而POST的语意是对指定资源“追加/添加”数据,所以是不安全的,每次提交的POST,参与的代码都会认为这个操作会修改操作对象资源的状态,于是,浏览器在你按下F5的时候会跳出确认框,缓存服务器不会缓存POST请求返回内容。


 

请求头

HTTP最常见的请求头如下:

l         Accept:浏览器可接受的MIME类型;

l         Accept-Charset:浏览器可接受的字符集;

l         Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间;

l         Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到;

l         Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中;

l         Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小;

l         Content-Length:表示请求消息正文的长度;

l         Cookie:这是最重要的请求头信息之一;

l         From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它;

l         Host:初始URL中的主机和端口;

l         If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答;

l         Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝;

l         Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。

l         User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用;

l         UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。

 

响应头

HTTP最常见的响应头如下所示:

l         Allow:服务器支持哪些请求方法(如GET、POST等);

l         Content-Encoding:文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面;

l         Content-Length:表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容;

l         Content-Type: 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。 可在web.xml文件中配置扩展名和MIME类型的对应关系;

l         Date:当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦;

l         Expires:指明应该在什么时候认为文档已经过期,从而不再缓存它。

l         Last-Modified:文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置;

l         Location:表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302;

l         Refresh:表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。

 

实体头

实体头用坐实体内容的元信息,描述了实体内容的属性,包括实体信息类型,长度,压缩方法,最后一次修改时间,数据有效性等。

l         Allow:GET,POST

l         Content-Encoding:文档的编码(Encode)方法,例如:gzip,见“2.5 响应头”;

l         Content-Language:内容的语言类型,例如:zh-cn;

l         Content-Length:表示内容长度,eg:80,可参考“2.5响应头”;

l         Content-Location:表示客户应当到哪里去提取文档,例如:http://www.dfdf.org/dfdf.html,可参考“2.5响应头”;

l         Content-MD5:MD5 实体的一种MD5摘要,用作校验和。发送方和接受方都计算MD5摘要,接受方将其计算的值与此头标中传递的值进行比较。Eg1:Content-MD5: <base64 of 128 MD5 digest>。Eg2:dfdfdfdfdfdfdff==;

l         Content-Range:随部分实体一同发送;标明被插入字节的低位与高位字节偏移,也标明此实体的总长度。Eg1:Content-Range: 1001-2000/5000,eg2:bytes 2543-4532/7898

l         Content-Type:标明发送或者接收的实体的MIME类型。Eg:text/html; charset=GB2312       主类型/子类型;

l         Expires:为0证明不缓存;

l         Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT.

 

扩展头

在HTTP消息中,也可以使用一些在HTTP1.1正式规范里没有定义的头字段,这些头字段统称为自定义的HTTP头或者扩展头,他们通常被当作是一种实体头处理。

现在流行的浏览器实际上都支持Cookie,Set-Cookie,Refresh和Content-Disposition等几个常用的扩展头字段。

l         Refresh:1;url=http://www.dfdf.org  //过1秒跳转到指定位置;

l         Content-Disposition:头字段,可参考“2.5响应头”;

l         Content-Type:WEB 服务器告诉浏览器自己响应的对象的类型。

eg1:Content-Type:application/xml ;

eg2:applicaiton/octet-stream;

Content-Disposition:attachment; filename=aaa.zip。


0 0
原创粉丝点击