HTTP 报文

报文的组成部分

HTTP报文 由起始行、首部、主体组成。

1、 起始行:

起始行是一个由行分隔的ASCII文本,每行都以一个由两个字符组成的行终止符作为结束,行终止符为 一个回车符 + 一个换行符,可以写作CRLF

2、 首部:

首部的格式与起始行相同

3、主体:

主体是一个可选的数据块,与起始行和首部不同的是,主题可以包含文本或二进制数据,也可以为空


报文的语法

所有的HTTP报文都可以分为两类:

1、请求报文:由客户端向web服务器请求一个动作

2、响应报文:向客户端返回请求的结果

例如,我们访问网址:http://jiayu.lu ,使用chrome浏览器,在地址栏中输入该网址并回车。此时按F12抓包的话,可以看见报文如下:

请求报文(请求头和请求行):

GET / HTTP/1.1
Host: jiayu.lu
Connection: keep-alive
Accept: text/html,application/xhtml+xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: __atuvc=0%7C10%2C0%7C11%2C0%7C12%2C0%7C13%2C1%7C14;

响应报文(响应头和响应行):

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: max-age=600
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Last-Modified: Mon, 15 May 2017 11:47:44 GMT
Vary: Accept-Encoding
Date: Tue, 23 May 2017 15:30:01 GMT
Transfer-Encoding: chunked

为什么我们不展示请求体和响应体呢?因为请求体的格式不一定是固定的,甚至有可能不存在请求体;而响应体,其实就是你看到的整个网页,只是浏览器帮你把它从html文本解析成了你所看到的页面

而抽象出来,请求报文的格式其实是:

<method> <request-URL> <version>
<headers>

<entity-body>

响应报文的格式是:

<version> <status> <reason-phrase>
<headers>

<entity-body>

这些格式有什么含义呢?

method(方法):

客户端希望服务器对资源执行的动作,是一个单独的词,例如 GET、HEAD、 POST、 PUT 等

而我们常说的的RESTful,其实就是通过不同的method来映射为不同的操作,从而简化接口约定、方便开发和理解

request-URL(请求URL):

命名了所请求的资源,或者是一个完整的URL。

如果是使用相对路径直接与服务器对话,服务器可以假定自己是URL的主机/端口,例如这里我们的请求url是 /,服务器会默认url请求的是本机的/ ,这通常不会有什么问题

version(版本):

报文所使用的HTTP版本,下面列出曾经或现在正在使用的HTTP版本

HTTP/0.9 已过时。只接受 GET 一种请求方法,没有在通讯中指定版本号,且不支持请求头。
由于该版本不支持 POST 方法,所以客户端无法向服务器传递太多信息。

HTTP/1.0 这是第一个在通讯中指定版本号的HTTP 协议版本,至今仍被广泛采用,特别是在代理服务器中。

HTTP/1.1 当前版本。持久连接被默认采用,并能很好地配合代理服务器工作。
还支持以管道方式同时发送多个请求,以便降低线路负载,提高传输速度。

HTTP/2.0  将来会普遍支持的版本,与HTTP 1.1相比,主要区别包括 :
    HTTP/2 采用二进制格式而非文本格式
    HTTP/2 是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
    使用报头压缩,HTTP/2 降低了开销
    HTTP/2 让服务器可以将响应主动“推送”到客户端缓存中

status(状态码):

不同的状态码代表不同的信息,例如我们常见的404(Not Found)、403(Forbidden)、3xx(Redirect)

一般来说,HTTP各状态码根据第一位数字进行分类,有一个简便的记法:
1xx : Hold on (等着)
2xx : Here you go (执行完了,没毛病,拿着结果回去吧)
3xx : Go away (你要的不在我这儿,去别处找)
4xx : You fucked up (你丫出问题了)
5xx : I fucked up (我特么出问题了)

reason-phrase(原因短语):

原因短语只对人类有意义,计算机只关心前面的状态码,所以返回的短语是啥,就按照人类思维去理解就对了

例如前面的响应行:
HTTP/1.1 200 OK
把它改成:
HTTP/1.1 200 NOT OK
此时浏览器依然当作是成功的,因为这里的状态码是200

header(首部)

header可以有任意个,每个header都包含一个名字,后面跟着一个冒号 : ,接着是一个值,最后是一个CRLF

首部是一个空行结束的,在首部的空行之后,就是主体了
header一般携带一些想让对方知道的简短信息,例如User-Agent、Cookie等,都放在这一部分。

如果你写了一个小爬虫,但抓出来的结果和想象的不同(例如界面样式等,或者是需要登录),你可以更改你发送的HTTP请求的header部分,将合法的User-Agent、Cookie等添上,能解决大多数此类问题,因为HTTP是无状态并且不安全的协议,所以这些小手段能够生效。

entity-body(主体)

主体包含一个由任意数据组成的数据块。

在请求报文中,主体可能是请求的参数(例如POST请求的参数)

在响应报文中,主体可能是一个html文档,即服务端想展示给浏览器的页面,
也有可能是一个普通的文本,具体类型和格式由服务端自行决定

并不是所有报文都包含主体部分,有时,报文只是以一个CRLF结束。

总结

总的来说,所谓HTTP协议,就是基于TCP之上的一种数据交换方式,而其饱含了各个字段的报文内容,对于TCP层来说,就是一段普通的文本而已,只是因为客户端和服务端彼此约定了使用HTTP的方式来解析,从而实现了统一。