HTTP协议详解

众所周知,HTTP 是我们通过浏览器上网所使用的主要协议,全称为 Hyper Text Transfer Protocol,超文本传输协议。

HTTP 是一个无连接无状态的协议。什么意思呢?

无连接就是每次传输数据完毕就立即断开连接,这样导致的后果就是如果请求同一台服务器上的多个资源,会花费很多时间在建立连接上。解决方法是在 HTTP/1.0 版本中使用 Connection: Keep-Alive; 字段来声明这是一个长连接,这样客户端就不会主动断开连接了。在 HTTP/1.1 中默认使用长连接。

无状态就是指每次传输的数据都是各自独立的,即当前的 HTTP 连接不知道它之前和之后的 HTTP 连接的所有情况。这样也会导致一些不好的后果,比如重复传输一些数据,如用户名和密码等。解决方法是使用 cookie 和 session。所谓 cookie 就是服务器发给浏览器的一个或多个字符串,用来在服务器端唯一标示该用户,它由服务器发给客户端并存储在客户端。而 session 则存储在服务器上,也唯一标示了该用户,服务器每次把该 session 的 sessionID 发给客户端,客户端将其返回给服务器就可以。

然而使用 cookie 和 session 的缺陷就是这是一种不安全的方式。因为不管是 cookie 还是 session,不管是 GET 还是 POST 方法,数据都是在网络上明文传输的,也就是说,攻击者可以获取你的 cookie 或 session 来伪造一个 HTTP 报头,让服务器误认为就是你本人在操作。针对这种情况,解决方案是使用 HTTPS 协议,它就是在 HTTP 协议和 TCP 协议之间加了一层 SSL 协议。SSL 是一个加密协议,服务器和客户端共同持有一个公钥和私钥,分别对发送的数据进行加密和解密操作。这样数据仍然是在网络上『裸』传输的,但即使攻击者拿到数据也因为没有私钥而无法解密,这就是一种很安全的方式。

HTTP 的请求报头格式如下(HTTP/1.0 以上版本):

请求方法 URL HTTP版本号 
请求头部字段 
[空行] 
消息正文

HTTP响应报头格式如下:

HTTP版本号 状态码 状态码的说明 
响应头部字段 
[空行] 
响应正文

HTTP/0.9 版本的请求报头格式如下:

GET URL

响应只能回复 HTML 格式的字符串,而不能是别的内容,比如

<html>
<body>hello world!</body>
</html>

而在请求报头中的请求方法共有9种,如下:

请求方法 含义 出现于哪个版本
GET 获取资源,不对服务器产生其他影响 HTTP/0.9
POST 向服务器提交数据,会影响服务器 HTTP/1.0
HEAD 和GET类似,只要求返回头部信息 HTTP/1.0
PUT 上传某个资源 HTTP/1.1
DELETE 删除服务器某个资源 HTTP/1.1
TRANCE 要求服务器返回原始HTTP请求的内容,可用来调试 HTTP/1.1
OPTION 查看服务器对某个特定URL都支持哪些方法 HTTP/1.1
CONNECT 用于某些代理服务器,将连接管线化 HTTP/1.1
PATCH 对某个资源做部分修改 HTTP/1.1

在这些方法中,GET、HEAD、TRANCE、OPTION 被视为安全的方法,因为他们只从服务器获得资源信息,而不对服务器做任何修改。而 POST、PUT、DELETE 和 PATCH 则影响服务器上的资源。

另一方面,GET、HEAD、OPTION、TRANCE、PUT 和 DELETE 等请求方法被认为是等幂的,即连续多次的,重复的请求和只发送一次的请求具有完全相同的结果。而 POST 方法不同,连续发送多次请求可能进一步影响服务器上的资源。

此外,Linux 提供了几个命令:HEAD,GET 和 POST。其含义与对应的同名请求方法相同,用来快速测试 web 服务器。

常见的头部信息有:

字段名称 含义
User-Agent 客户端用户代理
Accept 客户端可接受的文档类型
Content-Tyep 正文类型
Content-Length 正文长度
Last-Modify 最后修改时间
Content-Encoding 正文数据的压缩方法
Set-Cookie 传送给客户端的cookie

User-Agent字段

该字段用来表明客户端的标识信息,比如我的 chrome 浏览器就是 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36。所有头部字段都是这种格式,即 name: value 格式,需要说明的是,name 的冒号后面需要有一个空格,下同,这也是 HTTP 标准的一部分。

至于其他浏览器,你们可以自己去抓包查看。不再赘述。

Accept字段

该字段主要用来表明客户端可接受的文档类型,一般是 */*,即可接受任意类型的文档。

Content-Type字段

该字段用来表明正文的类型。因为 HTTP 标准规定,HTTP 报文的头部信息必须是 ASCII 格式的字符串,而正文可以是任意类型的,用此字段来说明。常见的有:

这些都是预定义的类型,除了这些,厂商还可以定义自己的类型,如 application/vnd.debian.binary-package 表明是 Debian 系统的二进制数据包。

HTTP/1.1

HTTP/1.0 的最大缺陷就是每个 TCP 连接只能发送一个请求,数据传送完毕,TCP连接也就关闭了。因此对于一个网页来讲,可能会有很多个资源需要获取,就需要创建很多个 TCP 连接,每次都需要三次握手四次挥手操作,效率较低。虽然可以声明 Connection: Keep-alive,但这毕竟不是标准定义的,属于扩展定义,不能从根本上解决问题。因此在 HTTP/1.0 发布后仅半年时间,HTTP/1.1 就发布了。

HTTP/1.1 最大的变化就是默认使用持久连接,不用声明 Connection 字段。即 TCP 连接不关闭,可以被多个连接请求复用。

管线机制

HTTP/1.1 还引入了管线化机制,即在同一个 TCP 连接里面,客户端可以同时发送多个请求,这样就进一步改进了 HTTP 协议的效率。

比如有两个连接请求,原来需要客户端先发送 A 请求,然后等待服务器响应 A 请求,再发送 B 请求,再等待 B 请求。在管线化里面,客户端可以先发送 A 请求,再发送 B 请求,然后等待服务器响应 A 请求和 B 请求,当然,响应顺序是不变的。

Host头部

HTTP/1.1 增加了 Host 头部字段,用来指明服务器的域名。这样就可以将数据发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。

HTTP/2

Google 于 2009 年公布了自行研发的 SPDY 协议,也就是 HTTP/2 的前身。

HTTP/2 的特性有:二进制协议,多工,数据流,头部信息压缩,服务器推送。

HTTP/3

截至写作本文,HTTP/3 还没有最终定稿,但总体方向已经确定。最大的变化就是抛弃长久以来使用的 TCP 协议,而改用 Google 发明的 QUIC 协议。QUIC 底层采用 UDP 作为传输层协议,目的就是通过自定义相关握手、挥手过程,拥塞控制算法等,从而大幅度提高性能。具体包括减少 RTT 次数而提升传输速度,分块传输增强抗丢包能力等。具体的 QUIC 协议以后若有时间可能会整理一下。

【完】

本文好多信息都是参考了阮一峰的博客,整理地很系统,条理清晰,十分推荐!点击这里,请阅读并背诵全文:)