HTTP协议详解

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

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

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

<!--more-->

无状态就是指每次传输的数据都是各自独立的,即当前的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种,如下:

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

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

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

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

常见的头部信息有:

<table> <thead> <tr> <th align="center">字段名称</th> <th align="center">含义</th> </tr> </thead> <tbody><tr> <td align="center">User-Agent</td> <td align="center">客户端标识</td> </tr> <tr> <td align="center">Accept</td> <td align="center">客户端可接受的文档类型</td> </tr> <tr> <td align="center">Content-Tyep</td> <td align="center">正文类型</td> </tr> <tr> <td align="center">Content-Length</td> <td align="center">正文长度</td> </tr> <tr> <td align="center">Last-Modify</td> <td align="center">最后修改时间</td> </tr> <tr> <td align="center">Content-Encoding</td> <td align="center">正文数据的压缩方法</td> </tr> <tr> <td align="center">Set-Cookie</td> <td align="center">传送给客户端的cookie</td> </tr> </tbody></table>

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格式的字符串,而正文可以是任意类型的,用此字段来说明。常见的有:

  • text/plain
  • text/html
  • test/css
  • image/jpeg
  • image/png
  • image/svg+xml
  • audio/mp4
  • video/mp4
  • application/javascript
  • application/pdf
  • application/zip
  • application/atom+xml

这些都是预定义的类型,除了这些,厂商还可以定义自己的类型,如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头部信息是纯文本的,数据部分可以是任意格式。而在HTTP/2中,全部都是二进制的,成为“帧”:头部信息帧和数据帧。
  • 在HTTP/2中,使用管线化机制时不必等待前面的请求的响应,服务器可以先发送处理完成的数据,比较耗时的数据延后发送。
  • 因为上一个原因,数据不是按顺序发出的,所以需要有一个标记了标识该响应是对哪一个请求而回应。且客户端发出的ID一律为奇数,服务器发出的一律为偶数。
  • HTTP是无状态的协议,因此出现了cookie和session。但每次都要发送cookie和session这些重复的数据会占用带宽,影响速度。因此可以将这些头部信息压缩后发送。
  • HTTP/2允许服务器主动将数据发送给客户端。

【完】

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