报文的组成部分
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的方式来解析,从而实现了统一。