文章大纲
引言
在当今互联网爆发的大时代,无论是Web程序还是App抑或是各种云服务等等要想发挥自身的价值都离不开网络,而理解程序之间通信的基本协议对于我们开发者来说相当重要,也是进阶的必经之路,这篇文章就好好总结下我们最熟悉的HTTP协议。
一、HTTP概述
1、HTTP的发展历程
HTTP(HyperText Transfer Protocal)超文本协议,用于定义万维网上各种超链接之间的通信规则,HTTP本身是无连接的,但是内部使用的是面向连接的TCP保证了数据的可靠传输,它的发展也是经历了几个历程,目前主流的还是HTTP/1.1。
2、HTTP协议的主要特性
- 支持C/S(客户端/服务器)模式——HTTP协议由两部分程序实现,一个客户端和一个服务器程序,通过交互HTTP报文进行通信
- 简捷快速——客户端向服务器发起请求的时候,只需要传递请求方法和路径即可,
- 灵活——HTTP允许传输任意类型的数据对象,简单数据如纯文本、字符串等,复杂如二进制、流等等,只需要设置正确的Content-Type标示。
- 无连接——即限制每次连接只处理一个HTTP请求,当服务器处理客户的请求并接收到客户的响应之后,立即断开连接,以节省传输时间。
- 无状态——因为HTTP服务器并不保存关于客户机的任何信息,即无状态协议。
3、HTTP的基本运作方式
HTTP运作是以事务为原子单位的,所谓事务即一个HTTP事务由一条HTTP请求(由客户端发往服务端)和一条响应(服务器接收到来自客户端的请求响应处理并发回给客户端),与数据库中的事务的概念类似,只有两条操作都完成之后才算成功。
4、HTTP 请求解析流程概述
- 首先经过DNS 协议映射到对应的IP地址
- 再经过TCP协议分割,并按照序列号可靠发出
- 借助IP协议,,通过路由器得到对应的接收IP地址
- 然后经过TCP协议接收分割后得到的报文段并重组
- 最终经过HTTP协议,返回服务器处理报文后的资源。
二、URI和URL
URI(Uniform Resource Identifiers),全称统一资源标识符,用于标志分布在整个因特网上的万维网文档,而URL(Uniform Resource Locator)统一资源定位符,
1、HTTP URI格式
形如:“http:” “//” hostname [ “:” port] [ biz_path ["?" query_params]]
- hostname:主机域名(主机Ip地址),必填
- port:端口号,非必须,若不填默认为80。
- biz_path:业务路径,非必须
- query_params:查询串,GET请求的时候直接附在上面的参数,形如?user=cmo&pwd=crazy
2、HTTP URI 匹配原则
一般情况下在进行URI匹配时候必须按照大小写敏感的规范逐一字节进行对比,但以下情况除外:
- 端口号为空或未给出的默认为80
- hostname 不区分大小写
- 协议名需要区分大小写,即http与HTTP不同,不过在有些浏览器会自动转成小写的
- biz_path为空相当于"/"
- 不属于保留或者不安全的字符相当于"%HEX HEX"格式的编码
3、 使用HTTP URI访问的基础流程
我们的浏览器给Web服务器发送了一个Request, Web服务器接到Request后进行处理,生成相应的Response,然后发送给浏览器, 浏览器解析Response中的HTML,这样我们就看到了网页
假设以浏览器访问http://www.crazy.com/index.html为例,经历以下步骤:
- 浏览器器解析URI,确认是有效的URI
- 向DNS服务器发起解析hostname——http://www.crazy.com的IP地址,再解析出端口号
- 紧接着DNS服务器返回IP,假设为8.8.8.8
- 接着浏览器与服务器(8.8.8.8)建立TCP连接
- 然后浏览器发起获取文件的请求报文: GET /index.html Host: www.crazy.com
- 服务器接受请求并给出响应报文
- 最后释放掉TCP连接,浏览器显示文档
4、URL的基本语法
形如: schemal " : //" “internet adress” [:port] “/resource_file_name”
- schemal:表面访问资源所采用的协议,比如http、ftp、smtp等。
- internet adress:服务器的因特网地址
- port:端口号,默认为80
- resource_file_name:完整的资源名带完整路径
几乎所有的URI都是URL。
5、连接和端口号
HTTP是个应用层协议,无需操心网络通信的具体细节,具体工作实际上是由底层的TCP/IP协议完成通信的,HTTP报文是通过TCP协议连接从一个地方传输到另一个地方去的。而在HTTP客户端向服务端发送报文之前,需要使用网际协议IP地址和端口号在两者之间建立一条TCP/IP连接,而TCP连接中,必须要知道对应的IP地址以及服务器上运行特定软件相关的TCP端口号。
6、HTTP/1.1持久连接
HTTP/1.1逐渐停止了对keep-alive连接的支持,取代的是一种名为持久连接(persistent-connection),达到的效果是一样的,但机制更优些。与HTTP/1.0的keep-alive连接不同,HTTP/1.1持久连接默认是激活的,除非特别指明。否则HTTP/1.1假定所有连接都是持久的,要在事务处理结束之后将连接关闭,HTTP/1.1应用程序必须向报文中显示添加上一个Connection:close首部字段,否则HTTP/1.1客户端在收到响应之后,连接依然维持在打开状态,但是客户端和服务器是可以随时关闭空闲的连接的,而且不发送Connection:close也并不是意味值服务器永远将连接保持在打开状态。
三、媒体资源类型MIME
由于因特网上有很多种不同类型的数据、资源,HTTP为了区分正确处理不同的传输对象,因此需要标记传输对象的类型,即MIME(Multipurpose Internet Mail Extension,多用途因特网邮件扩展) ,最初MIME是用于在邮件系统中表示不同类型的邮件的,HTTP借鉴并用来标记多媒体类型,在开发中比较常见的MIME类型
MIME | 文档 |
---|---|
text/html | HTML格式的文本文档 |
text/plain | 普通的ASCII文本文档 |
image/jpg | JPEG版本的图片 |
image/gif | GIF格式的图片 |
video/quicktime | Apple的QuickTime视频 |
application/vnd.ms-powerpoint | PPT格式的文档 |
四、HTTP请求命令
请求命令又可以叫做方法,每条HTTP报文都会包含一个方法,这些方法的将会协助服务器针对不同的方法进行不同的处理,比如说说获取一个Web页面、运行一个网管程序、删除一个文件等等,常见的有以下5种:
方法 | 说明 |
---|---|
GET | 从服务器向客户端发送命名资源 |
POST | 将客户端数据发送到一个服务器网关应用程序 |
PUT | 将来自客户端的数据存储到一个命名的服务器资源中去 |
HEAD | 仅发送命名资源响应中的HTTP报文的首部 |
DELETE | 从服务器中删除命名资源 |
五、状态码
HTTP/1.1中定义了5类状态码, 状态码由三位数字组成,第一个数字定义了响应的类别
-
1XX ——提示信息 - 表示请求已被成功接收,继续处理
-
2XX—— 成功 - 表示请求已被成功接收,理解,接受
-
3XX —— 重定向 - 要完成请求必须进行更进一步的处理
-
4XX —— 客户端错误 - 请求有语法错误或请求无法实现
-
5XX —— 服务器端错误 - 服务器未能实现合法的请求
六、HTTP报文
HTTP是通过报文来传输数据的,因而HTTP请求从一定程度上来说是不安全的,而HTTP报文本质上是一行行的简单文本格式的字符串构成的格式化文本,每一行都以两个字符——\r回车符(ASCII码13)和\n换行符(ASCII码 10)来组成的行终止标记序列CRLF。
通常按照HTTP报文的类别又可以分为两种类型:请求报文和响应报文,不过两种报文基本机构大同小异都包含三大部分:起始行、首部、主体。
1、起始行
起始行即报文的第一行,按照不同类别功能不一样:
1.1、请求行
在请求报文中请求行使用形如:< method > < request-url > < http-version > 格式用于说明这个请求要做些什么。
1.2、响应行
在响应报文中则是使用形如:< http version > < status-code > < respon-phrase > 用于表示服务器对请求的响应状态及结果。
2、首(头)部
首(头)部是用于标示主体数据的主机、类型、长度等信息的键值对, 起始行后面可以有一个或者多个首部字段(必须包含Host:xx),每个字段包含一个键和一个值,键值之间用冒号(:)来分隔连接(冒号之后是可以有一个空格),首部以一个空行CRLF结束,当然首部也可以分为多行来描述,但多出来的每行前面必须要有一个空格或者TAB符。
3、主体
首部下的空行之后就是报文主体,在请求报文中则包含了所有要发给服务器的各种类型的数据;而在响应报文中则是装载了要返回给客户端的数据。总之,报文的主体就是一个由任意数据组成的数据块,但并不是所有的报文都包含实体的主体部分,有些报文也可以单纯以一个CRLF结束。值得注意的是,起始行和首部都是纯文本形式且结构化的,而主体则可以包含任意的数据——纯文本和二进制数据(比如图片、视频、音轨、软件程序等等),最后与起始行和首部都不同,主体也还可以为空,一般来说Get的请求报文的主体可以为空,而Post请求则会把参数以类似请求头的K-V形式存在请求报文的主体中。
最后请看下以下实例的报文图:
VS
当在解析请求的时候可能没有Content-Length而是遇见的Transfer-Encoding响应头,主要有编码方式:
- Transfer-Encoding: chunked——数据以一系列分块的形式进行发送。 Content-Length 首部在这种情况下不被发送。。在每一个分块的开头需要添加当前分块的长度,以十六进制的形式表示,后面紧跟着 ‘\r\n’ ,之后是分块本身,后面也是’\r\n’ 。终止块是一个常规的分块,不同之处在于其长度为0。终止块后面是一个挂载(trailer),由一系列(或者为空)的实体消息首部构成。
- Transfer-Encoding: compress——采用 Lempel-Ziv-Welch (LZW) 压缩算法。这个名称来自UNIX系统的 compress 程序,该程序实现了前述算法。
与其同名程序已经在大部分UNIX发行版中消失一样,这种内容编码方式已经被大部分浏览器弃用,部分因为专利问题(这项专利在2003年到期)。 - Transfer-Encoding: deflate——采用 zlib 结构 (在 RFC 1950 中规定),和 deflate 压缩算法(在 RFC 1951 中规定)。
- Transfer-Encoding: gzip——表示采用 Lempel-Ziv coding (LZ77) 压缩算法,以及32位CRC校验的编码方式。这个编码方式最初由 UNIX 平台上的 gzip 程序采用。处于兼容性的考虑, HTTP/1.1 标准提议支持这种编码方式的服务器应该识别作为别名的 x-gzip 指令。
- Transfer-Encoding: identity——用于指代自身(例如:未经过压缩和修改)。除非特别指明,这个标记始终可以被接受。
七、服务器解析HTTP请求的基本流程
一般情况下,无论是小型服务器还是大型先进的商用服务器的业务流程都差不多:建立连接——>接收请求——>处理请求——>访问资源——>构造响应——>发送响应——>记录事务处理过程。
1、建立连接
如果客户端已经打开了一条道服务器的持久连接,可以使用那条连接来发送它的请求,否则客户端需要打开一条新的到服务器的连接。当客户端发起一条到服务器的TCP连接时,服务器就会建立连接,并判断连接的另一端是哪个客户端,从TCP连接中将IP地址解析出来,一旦新连接建立起来并被接受,服务器就会自动将新连接添加到服务器连接列表中。
2、接受请求并解析HTTP报文
连接上有数据到达时,服务器会从网络连接中读取数据,并将请求报文的内容解析出来。在解析请求报文的时候,服务器还会不定期地从网络上接收输入数据。
- 首先解析请求行,查找请求方法、指定的资源标识符(URL)以及版本号,各项之间由一个空格分隔,并以一个回车换行(CRLF)序列作为行的结束标志。
- 接着读取以CRLF结尾的报文首部
- 再接着检测到以CRLF结尾的、标志首部结束,主体开始的空行。
- 再解析长度(如果设置了Content-Length),读取请求主体。
3、访问报文中指定的资源
4、构建并发送响应
一旦服务器识别出来资源,就会执行请求方法中对应的动作并响应报文,一般报文中包含有响应状态码、响应首部,响应主体。值得注意的是服务器并不是每次都一定会返回成功的报文,有时候也会返回重定向响应(由响应码3xx表示),重定向响应可以使得度武器将浏览器重定向到其他地方执行请求。
PS:下一篇使用原始Sockect 发送Http请求。