网络协议(一)HTTP协议(上)

HTTP

  对于HTTP协议,我们或多或少都知道它是HyperText Transfer Protocol,也就是所谓的超文本传输协议,但其实它里面有很多可以深究的地方。

  在了解HTTP之前,我们需要知道一件事,HTTP协议实际上我们随处可见,甚至说,我们现在所处的网络下底层运转就是靠HTTP来运转的。

  那么提到HTTP,我们就得提到万维网之父蒂姆·伯纳斯·李,是他提出了在互联网上构建超文本系统,也就是将超文本技术应用到计算机当中。后来,万维网和HTTP就因此而诞生了。这中间的过程在这里不过多阐述,网络上或者书本上都有相关介绍。在这之中他也提出了三个概念:URI、HTML以及HTTP。

  接下来我们先来了解一下HTTP的发展历程。

 HTTP的发展历程

   在最早期的时候,HTTP还没有版本号,我们所熟知的HTTP1.0/1.1都是后续添加的版本号。而实际上在1.0之前,还有一个版本号HTTP/0.9。

   HTTP/0.9呢,它在响应格式上是纯文本形式的,并且也就只有“GET”这个方式去获取HTML的内容,是一个受限于时代发展的版本。而后来随着计算机多媒体技术的发展,HTTP协议也顺势进化,就出现了HTTP/1.0版本。

   在HTTP/1.0版本,向响应中加入了响应状态码。并且除了原有的GET方式以外,又增加了POST等方式。还有我们耳熟能详的Header,也是在这个版本中诞生的。在HTTP/1.0正处于热火状态的时候,发生了著名的网景与微软的浏览器大战,又再次推动了HTTP协议的发展,至此HTTP/1.1版本就诞生了。

   那么HTTP/1.1版本中又有什么变动呢?最主要的几个变动如下:增加了缓存管理、连接管理、对响应数据进一步分块等等。

   我们必须要说的就是这个HTTP/1.1版本,它实际上是对HTTP/1.0维护,但它具有HTTP/1.0版本没有的意义,那就是HTTP/1.1是一个标准,铁则。这是什么意思呢?就是只要互联网用到HTTP协议,我们就得遵守HTTP/1.1版本的规矩,比如浏览器、服务器以及代理等。

   在当时HTTP/1.1版本已经够用了,但是随着互联网的发展,HTTP/1.1版本已经有点力不从心了,后来谷歌就在这之中开发了Chrome浏览器,并且提出了一个新协议SPDY。而这个SPDY协议呢,就是HTTP/2的前身,或者说是HTTP/2的基底。

   在HTTP/2中对性能做了很大的提升,比如说将协议改为了二进制协议,新设计的压缩算法等。对于HTTP/2,实际上是对语义,语法进行了一定的划分。但是谷歌并不满足,它又提出了一个新的协议叫gQUIC,并且在前几年成功的成为了HTTP/3的前身。

   而关于HTTP3的RFC还没有正式出版,但可以先知道它是一个基于UDP的多路复用以及安全传输协议。

  对于HTTP的发展历史,如果有更多的兴趣可以再去详细了解,这里只对它进行一个概述。那么,当我们了解完HTTP的发展历程以后,我们就可以步入正题:HTTP到底是什么?

 到底什么是HTTP?

   我们先来看一下HTTP的全称:HyperText Transfer Protocol,中文翻译过来就是超文本传输协议。

   所谓的“超文本”,就是文字、图片、音频以及视频等的混合“文本”。对于超文本,我们特别熟悉,比如说HTML,是内部带有各类标签的纯文本样式,是超文本的载体。

   那么再来看“传输”,传输其实特别好理解,就是从A到B或者从B到A,也就是所谓的双向传输,但是不限于AB之间。这是什么意思呢?就是在A和B之间,还可以掺杂着C或者D。换句话来说,可以有中介,比如我们所知的CDN、代理等。而实际上在HTTP中“传输”的意义并不在于行为,而在于传输的报文内容

   最后再来看HTTP中最重要的部分:“协议”。何为协议,就是对两个或者两个以上的参与者的行为规范和协商。

   那么其实综合来看,我们可以得到一个结论:HTTP是一个在计算机中用于规范计算机之间传输数据行为的协议。

   那么我们知道HTTP是处于应用层的一个协议,在他之下还有其他协议。比如说TCP/IP协议以及SSL/TSL等等,对于这部分的内容,再新开一章讲述。

  在对HTTP剖析之前,我们需要先了解一些相关的概念。

 相关概念

  DNS

   DNS(Domain Name System),中文名为域名系统

   对于域名,它可以说是IP地址的抽象化,换句话来说,域名就是物理网卡的MAC进一步抽象化的体现。那么更进一步地说,实际上域名就和编程中的变量一样,都是一个命名空间,只不过变量是一段连续的内存空间别名,而域名则是IP地址的别名。

   那么到底域名具有什么样的表现形式呢?我们以www.csdn.net为例,我们可以看到它是一段字符串,并且在字符串中还带有“.”用于分割,每个“.”分割的部分都代表了不同层级的域名。

   我们把最右边的部分,也就是net部分,称为顶级域名,并且层级向左依次降低。对于顶级域名下一层的二级域名,则是用于区分整个域名所属的单位,比如说属于csdn的bbs,属于163的bbs。而最左边的部分,称他为主机名,则是用于表示该主机的用途,也就是提供什么样的服务,比如说“www”表示该主机提供万维网服务。

   而域名系统它的内部,也是这样的一个构造,如图所示:

   最顶层的就是根域名服务器,它的作用是管理如com、net等这样的顶级域名服务器和返回相应的顶级域名服务器所代表的IP地址。

   而在根域名服务器下一个就是顶级域名服务器,它的作用就是管理如csdn、tencent等这样的权威域名服务器,并返回这些权威服务器所对应的ip地址。

   而第三层则是权威域名服务器,它的作用就是管理自己域名下的主机的ip地址。

   通过这样的构造,域名系统向外提供了一个域名解析服务。因为在上面的时候说过,域名只是IP地址的别名,实际上还是需要通过MAC来访问主机。因此,我们需要解析域名来获得相应的IP地址,再进一步解析IP地址为MAC地址来达到访问主机的目的。

   当我们在浏览器输入一个域名按下回车访问时,这个域名就会在这个分层结构中从上到下查询,直到获取了域名相对应的IP地址。比如说,我们访问“www.csdn.net”就会进行以下三次查询:

1.先访问根域名服务器,得到“net”顶级域名服务器的IP地址。
2.再访问顶级域名服务器,得到“csdn.net”权威服务器的IP地址。
3.最后访问权威服务器,得到“www.csdn.net”的地址。

   域名解析系统出现了以后,这样就足够了吗?实际上,随着在互联网上冲浪的人数越来越多,只有这样一个核心系统并不能满足需求,因此,“域名缓存”应时而出。

   所谓的域名缓存就是,像谷歌之类的大头公司或者网络运营商,建立自己的DNS服务器。当用户访问这个DNS服务器时,如果在这之前已经查询过这个域名的ip地址,就会直接返回该域名对应的IP地址,反之则会代替用户去访问根域名服务器,查询到相应的ip以后,会将这个ip地址缓存下来然后返回给用户。

   而在操作系统中,它会缓存对DNS解析后的结果,也就是对域名相应的IP地址进行缓存。在操作系统中有一个特殊的文件hosts,它就是用于保存主机映射的相关内容。也就是说,当我们第二次访问“www.csdn.net”,它就不会再去DNS找相应的IP地址,而是直接通过操作系统中这个hosts文件拿到相应的IP地址。

   实际上,除了上述所提到的DNS服务器和操作系统以外,我们所用的浏览器内部也会缓存DNS。

   通过这样的缓存思想,我们就可以减轻域名解析的压力,提升用户的体验。

   最后,我们来总结一下浏览器域名解析的一个过程,如下所示:

    1.浏览器发现地址栏中是域名(非数字)地址,检查浏览器缓存中是否有该域名对应的解析过的IP地址,如果有,结束域名解析。如果没有,则进行下一步。
    2.通过访问hosts文件查找操作系统中是否有缓存该域名对应的IP地址,如果有,结束域名解析。如果没有,则进行下一步。
    3.当hosts文件中没有缓存,那么就去找本地域名服务器(网络配置中的DNS服务器配置),也就是提到的大公司或者运营商提供的服务器,找相应的域名解析结果。如果有,结束域名解析。如果没有,则进行下一步。
    4.当浏览器、操作系统以及本地域名服务器都没有拿到相应的域名解析结果,就会向根目录域名服务器获取域名解析结果。并且缓存到本地

   通过上面的阐述,我们就知道DNS的作用以及浏览器是如何从地址栏中得到输入的域名地址相对应的IP地,然后再进行相应的请求-应答过程。

  URI

   URI(Uniform Resource Identifier),中文名称是统一资源标识符。需要注意的是,它和URL(Uniform Resource Locator,统一资源定位符)特别相似,一般不会做严格区分,两者在样式是一致的,但其实在意义上并不是等同。

   实际上,URI是URN(统一资源名)和URL的合集,URI更准确来说,是一个用来标识抽象和物理资源,而URL则是除了确定资源以外,还需要提供定位该资源的访问机制。

   这里贴上维基百科中的一段解释:

统一资源名(URN)如同一个人的名称,而统一资源定位符(URL)代表一个人的住址。换言之,URN定义某事物的身份,而URL提供查找该事物的方法。

   我们看一下URI最常用的一个形式,如下图所示:

   URI第一个组成部分是协议名,表示待访问资源应使用哪种协议访问,比如说http、https以及ftp等等,浏览器辨识到相关协议时,就会调用相应的协议的API去做进一步的处理。而对于://这一段特定的字符串,则是用于区分协议名和后面的字符串部分。

   第二个组成部分就是表示访问的资源,里面包含了资源所在的主机以及在主机上的位置。对于主机名,在网络通信协议中,它是必须存在的,而端口如果没有表明,就会根据协议名来使用默认的端口号。在这里需要注意的是:URI中的path部分必须以斜杆开头,也就是图中的红色加粗斜杠

   第三个组成部分则是查询参数,表示对资源定位的附加要求。比如说,我们在使用搜索引擎的时候,会在地址栏看到path后面跟了多个参数,比如说百度的wd亦或者csdn中的p。

   所以有时我们会看到地址栏会有特别长的字符串,都是由这三部分拼接而成的URI,它的作用就用于定位资源。

 HTTP剖析

   在了解完相关的概念以后,接下来我们对HTTP进行剖析。

  环境搭建

   为了更好研究HTTP,接下来,我们通过几个软件来搭建一个演示环境,如下所示:

一个请求方——浏览器,Chrome或者Firefox。
一个应答方——服务器,这里用Linux中的Nginx代替。
一个抓包工具——Wireshark。它用于截取TCP/IP协议栈中传输的流量

   对于软件的下载安装在这里不予以讲述,自行查询。在安装好相应的软件以后,为了能够通过域名访问到这个主机,我们还需要在客户端中的hosts文件中添加相关的信息,如下所示:

192.168.0.211 www.cexample.com

   那么接下来我们启动Nginx,访问该网页,结果如图所示:

   可以看到我们可以访问到该页面,那么接下来我们使用WireShark开始进行抓包处理。

  HTTP初剖析

   需要注意的是,这里我们过滤器需要设置为HTTP TCP PORT(80)用于只抓取和HTTP相关的包,如图所示:

   接下来我们进行抓包,打开浏览器访问192.168.0.211,当响应结束以后,我们将抓包停止,结果如下所示:

   需要注意的是:有多个TCP流时,可以在上方的过滤器中填入表达式:tcp.stream eq 0 筛选出第一个完整的TCP流。

   首先我们可以从图中看到,当我们在浏览器的地址栏中使用了HTTP访问192.168.0.211时,它会先建立TCP连接,这是因为HTTP协议就是运行在TCP/IP协议之上的。那么,根据TCP协议,浏览器需要经过“三次握手”和服务器建立可靠的连接。也就是抓取的数据包中的前三个,如图所示:

   可以看到,通过SYN,SYN/ACK,ACK三个包以后,浏览器和服务器的TCP连接就建立起来了。在建立好TCP连接以后,浏览器就向服务器发送了一个格式化的报文,也就是数据包中的第四个包,如下所示:

   当web服务器接收到报文后,就会解析报文并且进行相应的请求处理行为。那么这里我们是访问根路径下的默认文件index.html,那么web服务器就会将这个文件的内容再根据HTTP规定的格式发送给客户端,也就是以下这个包:

   当浏览器收到这个包的时候,会向服务器发送一个ACK,告诉服务器已经接收到了发送的响应。那么当接收到这个响应报文后,浏览器就会去解析这个报文。

   当解析到是一个HTML文件的时候,浏览器就会调用内置引擎去处理这个文件,并且将内容渲染给用户。

   而最后四个包则是“四次挥手”,不是本文的重点,在此略过。

   那么这上面的整一个过程,就是浏览器接收到用户的请求后,向服务器发起HTTP请求后的一个简单过程。我们通过域名访问也是同样的结果。

   在前面的时候也说过,在传输当中可以有中介,比如说CDN亦或者负载均衡服务器。那么,在现实环境中,并不是上面这么纯粹简单的传输过程,这里只是演示最简单的过程,只需要知道HTTP传输,客户端和服务端大致都做了什么即可。

   在了解完HTTP的请求-应答以后,我们再来了解在上面提到过HTTP的传输的核心:传输的报文内容。接下来我们就来了解一下这个请求报文中的具体结构。

  HTTP再剖析

   HTTP传输的报文分为两种:请求报文响应报文,而一个完整的HTTP报文如下所示:

   对于起始行和头部,我们根据两类报文又分别合称为“请求头”和“响应头”,而消息正文又称为“实体”(body)。HTTP协议规定,报文中可以没有实体内容,但必须有header,比如get请求获取资源。并且,在header的下一行必须为空行(CRLF)。

 请求报文

   接下来,我们来看一下刚刚包中的GET / HTTP/1.1,如下所示:

   如图上所示,第一个橙色框中的GET / HTTP/1.1就是请求行,在第一行下面的黄色框内的就是header,而最后红色框的是一个\r\n,也就是一个CRLF。

   接下来我们分别对这三个部分进行讲解,首先是请求行,我们点开该列表项,显示如下:

   请求行由三部分组成,从左到右分别是请求方法、请求URI以及HTTP协议版本号,如下所示:

   请求URI用于定位资源,请求方法表示服务端对资源的操作方式。HTTP/1,1中定义了八种方法,如下所示:

请求方法含义
GET获取资源
HEAD获取资源的元信息
POST向资源提交数据(非幂等)
PUT向资源提交数据(幂等)
DELETE删除资源
CONNECT建立特殊的连接隧道
OPTIONS列出可对资源实行的方式
TRACE追踪请求-应答的传输路径

   需要知道的是:请求方法是客户端向服务器发送操作资源的请求,并不是客户端就能够决定服务器执行该操作,服务器是有权力拒绝客户端的请求

  GET/HEAD

   那么接下来我们来稍微了解一下前面四个请求方法,首先是GET方法,它表示请求从服务器获取资源。一般来说,获取的资源大多数是一个页面,页面可以是静态的也可以是动态的。而资源还可以是其他格式的,比如说JSON字符串。

   那么实际上,GET方法只是客户端告诉服务器:我要申请URI表示的这个资源,但是返回给客户端的数据是由服务器的实现来决定的。

   而HEAD方法和GET方法近乎一致,无论是本质上还是服务器的相应处理上,它们的差别在于返回体。当服务器解析到请求方法为HEAD并做了一定的处理后,它会返回给客户端响应,但是这个响应里面只有响应头,没有响应体,这在一定程度上避免了传输实体数据的浪费。

   那么讲完了GET/HEAD两兄弟以后,我们就要来讲POST/PUT两兄弟了。

  POST/PUT

   POST方法和GET方法相反,它是向服务器发送数据,也就是说,它是告诉服务器一些信息,这些信息保存在报文的请求体中。

   这就好比我们使用聊天软件,按下“发送”按钮时,会将输入框中的文字放进报文的请求体里,然后拼接上相应的请求头,发送给服务器。

   而PUT方法和POST方法有些许类似,也是向服务器发送数据,但是通常我们将POST表示创建一个资源,而PUT表示修改一个资源。实际应用大多数以POST为主。

   在上面的表格中还附加了幂等和非幂等。所谓的幂等就是不管进行多少次重复的行为,都会实现相同的结果。实际上除了POST不是幂等方法以外,其他方法都是幂等方法。

   在请求行的下面就是首部字段,这些字段和请求行一起构成了HTTP报文中的请求头。

   首先我们来看它的组成,首部字段由多个键值字符串组成,键和值之间用冒号分割。特别需要知道的是:首部字段除了用HTTP规范的已有字段,还可以任意地添加自定义的字段。

   HTTP协议规定了特别多的首部字段,这些字段都有各自的用处并且大多数的字段都有重要的作用。比如说,Accept-Charset是告知服务器客户端能接受的字符集。再比如Cache-control字段,它是用于控制缓存的,和max-age一起发挥作用。

   我们来看一下刚刚那幅图上的首部字段,如下所示:

   这里挑几个首部字段来讲,首先是最重要的Host字段。为什么说它最重要呢?因为如果报文中没有Host字段,就不是一个正确的报文。这是因为HTTP规范中规定Host字段有且仅能出现在请求头里。

   Host字段是用于告诉服务器应该是哪台主机去处理当前请求,比如在192.168.0.211上有多个虚拟主机:www.cexample.com、www.scorpion.com等等。当我们通过域名访问时,就得告知服务器我们访问的是哪一个域名,也就是指明Host字段。如果不指明Host字段,服务器就不能让域名对应的主机去处理请求。

   下面的Connection字段它的用处有两个:可以用于控制不再转发给代理服务器的首部字段、可以用于管理长连接。在HTTP/1.1开始这个字段的值默认就是keep-live,也就是长连接。

   我们再来看User-Agent,它的作用就是告诉服务器,发起HTTP请求的客户端哪一个浏览器,然后服务器根据这个字段的值来返回该浏览器能显示的页面。

   最后我们再来看一个在图中并没有出现的一个字段Content-Length。实际上它属于和实体有关的字段,它表示报文中实体的长度,也就是请求头或响应头空行后面数据的长度。如果没有指定该字段,服务器就会用chunked编码的方式来分段传输数据。

 响应报文

   其实响应报文和请求报文没有多大的区别,他们可见的区别就在于第一行。请求报文中将第一行称为请求行,而响应报文将第一行称为状态行。

   状态行表示的是服务器接收到请求后响应的状态,它的结构如下所示:

   我们对照着抓包中的响应报文中的状态行来看,如下所示:

   其中,HTTP/1.1就是报文使用的HTTP协议的版本,200就是状态码的一种,表示请求已经收到并且会返回请求需要的资源。而后面的OK,则是表示对状态码的解释说明。

   接下来我们来看一下在状态行中最有用的状态码,它表示服务器对请求的一个处理结果,可以是“正确”的结果,也可以是“错误”的结果。也就是说,无论返回的状态码是什么样的,客户端都可以根据状态码来做相应的调整处理

   状态码由三位数字组成,其中,第一位表示状态码的类别,剩下两位用于区分同类不同种状态的描述。在RFC标准中规定了状态码有五类,如下所示:

状态码类别具体含义
1xx系列临时响应状态码,表示当下时刻是协议处理的中间状态,需要进一步的操作
2xx系列成功状态码,表示请求报文已经收到并且正常处理完毕
3xx系列重定向状态码,表示客户端请求的资源发生了变动,需要进行更多操作来实现请求
4xx系列错误状态码,表示客户端发送的请求报文有误
5xx系列错误状态码,表示服务器内部错误

   RFC规范中有50多个状态码,但实际上我们在使用一些web服务器的时候,我们会发现除了RFC规范里的,还有不在规范中的状态码。比如说Nginx中的499,它表示服务器请求过多,客户端在有效时间内没有得到响应,就自行关闭连接。

   那么,接下来详细介绍一部分状态码。

  1xx系列

   对于1xx类的状态码,我们只了解一个101,它是指根据首部字段Upgrade来提升协议。意思就是,客户端在请求报文中附加Upgrade首部字段告知服务器需要升级协议,而服务器也会相应的返回101状态码告知客户端也要升级协议。比如说从HTTP/1.1到HTTP/2或者从HTTP协议到WebSocket协议的变化。

  2xx系列

   对于2xx类的状态码,我们在学习中最常见的就是200,它表示服务器返回了客户端想要的响应结果。和200有相近含义的就是204,它同样也是表示返回了想要的响应结果,但是响应报文内没有实体数据,也就是说,响应头后面只有空行没有body。

   还有一个比较常见的状态码206,它和下载有关系,也同样是表示成功的含义,但是它更多表示的是服务器已经处理了部分GET请求。这个状态码是HTTP分块下载或断点传输的基础,比如说迅雷这类HTTP下载工具,都是使用这个来实现分块下载和断点续传的功能。

   这个状态码206,往往会需要请求报文中包含Content-Range首部字段来告诉服务器,客户端希望得到的内容范围,也就是服务器返回的响应报文中实体数据的具体范围。除此之外,请求报文中还可以附带If-Range首部字段来限制范围请求。

   那么从上面的描述中,可以知道206是范围请求的成功标识。如果请求的范围超过了资源总大小,也就是越界情况产生的时候,服务器会返回一个416状态码。这里需要注意的是,如果服务器不支持范围请求这样的响应机制,就会直接返回一个200状态码并且附带整个实体数据给客户端。

  3xx系列

   说到3xx类状态码,我们最熟悉的就是301、302,301状态码表示永久重定向,而302则是临时重定向。

   对于301和302,服务器都要在响应头中使用Location首部字段来指明要重定向的URI,它们的表现形式都是跳转到另一个URI上。

   那么到底什么是永久重定向和临时重定向呢?永久重定向就是指此次请求的资源不存在,需要改用另一个URI再次访问。而临时重定向则是此次请求的资源仍然存在,但是需要暂时用另一个URI来访问。

   描述看起来很相似,其实实际上在表现上就非常明显。比如说,我们访问a.html的时候,服务器返回了一个301状态码给客户端,并且附带一个Location:b.html,那么我们就会在地址栏上明显地看到地址栏发生了变化:从a.html变为b.html。而如果返回302状态码给客户端,我们会发现地址栏依旧没有变化,仍然是a.html。

   进一步地说,其实301重定向能够告诉用户网站域名的变动,而302重定向无法告知用户网站域名已经变动了。

   特别补充一点:当浏览器收到302状态码的时候,它就不会做缓存优化这个操作,也就是说,下一次请求这个资源的时候,仍然用原来的地址访问。

   在3xx系列中还有一个不太名副其实的304状态码,因为它并没有具备上述301、302的那种跳转含义,但是又称它为缓存重定向,也就是重定向到已缓存额文件。

   这里再次抓包,结果如下所示:

   可以看到,我们访问默认根路径下的index.html在上一次访问已经缓存了,再次请求这个资源的时候,服务器给我们返回了一个304状态码。

  4xx系列

   谈到4xx系列的状态码,或许我们就会直接想到404状态码,毕竟这是我们最常看见的一个状态码了,也是我们最不想看到的状态码。

   在讲404之前,我们先来讲一下403。4xx系列默认都是客户端的请求发生了错误,而403实际意义却是服务器禁止该请求,一般相应的会返回一个拒绝请求的页面,上面会说明拒绝的理由。

   那么404呢,原本的使用情况是当请求所希望得到的资源在服务器上没找到的时候,服务器就会返回这样一个状态码,但是现阶段被模糊化了。这是因为可能请求的资源没找到,也有可能是拒绝访问该资源,原因无从而知,所以其实这个状态码已经被模糊化了。

   其实除去上述这两个已经被模糊化的状态码以后,剩下的4xx系列状态码是非常好理解的,比如说405(Method Not Allow)、408(Request Timeout)或者429(Too Many Requests)以及401(Unauthorized)。

  5xx系列

   如果客户端请求报文正确的情况下,服务器处理时内部出了问题,就会返回5xx系列的状态码。

   我们需要知道的是,服务器返回出错的状态码,不应该过于明文告知客户端,这是基于安全性的考虑。所以相比于4xx系列中的400来说,5xx系列的500就是非常有必要的。

   500状态码(Internal Server Error)它不会告诉客户端服务器究竟发生了什么样的错误。

   而其他的5xx系列状态码就非常浅显易懂了,比如说501(Not Implemented)可以表示该功能未实现。再比如说502(BadGateWay),它表示作用为网关或代理的服务器访问后一个服务器时发送了错误。还有一个最常见的状态码503(Service Unavailable),在高并发场景下通常会看到,表示当前服务器繁忙,无法响应请求。

   最后,我们来总结一下状态码:我们可以将状态码理解为客户端和服务端沟通的语言。状态码不是只需要对其中一个负责,而是需要对客户端和服务端两者共同负责。

 报文实体

   在上面了解了两类报文的头部,而它们的实体数据还没有述说,那么接下来我们开始了解HTTP中报文的实体部分。

   对于实体部分,它可以包含多种不同的内容,比如说文本、图像以及音频等等。那么在这么多种内容的情况下,作为客户端或者作为服务器,也就是上层应用,获取到了实体以后,如何才能知道实体部分到底属于哪种类型的数据呢?

   那就是通过MIME(Multipurpose Internet Mail Extensions)规范,也就是所谓的多用途互联网邮件扩展,来告知上层应用HTTP报文中实体内容的数据类型,而在报文中就通过首部字段Content-Type来表明。

   Content-Type的值以斜杠划分,斜杆前表示数据类型,斜杠后表示数内容的格式。比如说最常见的text/html,前面是文本的数据类型,后面是实体内容是html。

   有了Content-Type就能让上层应用知道实体数据中到底是什么,但是往往我们传输的数据可能非常的大,为了传输效率,就会将内容压缩。而如果我们将内容压缩了,上层应用就不知道这个数据原本的样子是什么。因此,HTTP中还提供了一个Content-Encoding。

   Content-Encoding的值我们最常见的就是gzip亦或者deflate,对于它们的区别在这里不讲述。

   这上面所讲的Content首部字段都是HTTP协议中服务器用来告诉客户端发送了什么类型的数据,而我们说过协议是对两者之间的规范和协商,那么同样的,客户端也理应告诉服务端自己能接收什么类型的数据。

   因此,HTTP协议还提供了和Content-Type相对的首部字段Accept以及和Content-Encoding相对的首部字段Accept-Encoding。

   接下来,我们来看一下上面抓的请求包和向响应包中的首部字段,如下所示:

   这里需要注意的是,虽然图中Content-Type只有text/html,实际上它后面还能跟一个charset,如下所示:

Content-Type: text/html; charset=utf-8

   看到charset和utf-8就应该明白这是在指定字符集,但是实际上现在这个值已经不太经常使用了,只需要知道有这一个东西就好。

   这里比较值得说的是,在图中我们可以看到一个很奇怪的字符q,这是代表了权重的意思,也就是quality factor,它是用于表示优先级,最大值为1,最小值为0.01。它的值还可以为0,指的是拒绝这类文件。

   比如说,图中的这一段:

Accept: text/html,[中间省略]application.xml;q=0.9

   如果q没有指定的时候,就默认为1,也就是最大优先级。这段就表示浏览器最希望接收到的数据格式是HTML文件,其次是xml文件,依次顺序下去。服务器收到这样的请求头的时候,会进行权重的计算,然后输出相应的格式给客户端。

结束语

	'''
		在这一章只是初步的对HTTP做了一个稍微详细的介绍
		实际上HTTP包含了非常多的东西,也牵扯非常多的知识
		应用层的协议不止HTTP,还有FTP、SSH等等,但是能够独占鳌头的却是HTTP
		这是因为HTTP协议是一个非常开放的传输协议,比如说任意首部字段、状态码、实体数据等等
		当然HTTP也有它不好的地方,比如“明文传输”,比如“完整性校验”,还有和它夕夕相伴的“无状态”
		在接下来的一章将会讲述HTTP中几个内置机制:连接管理、缓存代理等等
	'''
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值