HTTP 协议也是与 TCP/UDP 类似,同样也需要在实际传输的数据前附加一些头数据,不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。
HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
1.起始行(start line):描述请求或响应的基本信息;
2.头部字段集合(header):使用 key-value 形式更详细地说明报文;
3.消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。
这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。
HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”。所以,一个完整的 HTTP 报文就像是下图的这个样子,注意在 header 和 body 之间有一个“空行”。
例如:
在这个浏览器发出的请求报文里,第一行“GET / HTTP/1.1”就是请求行,而后面的“Host”“Connection”等等都属于 header,报文的最后是一个空白行结束,没有 body。
请求行
请求行由三部分构成:
1.请求方法:是一个动词,如 GET/POST,表示对资源的操作;
2.请求目标:通常是一个 URI,标记了请求方法要操作的资源;
3.版本号:表示报文使用的 HTTP 协议版本。
如:
GET / HTTP/1.1
在这个请求行里,“GET”是请求方法,“/”是请求目标,“HTTP/1.1”是版本号
状态行
看完了请求行,我们再看响应报文里的起始行,在这里它不叫“响应行”,而是叫“状态行”(status line),意思是服务器响应的状态。比起请求行来说,状态行要简单一些,同样也是由三部分构成:
1.版本号:表示报文使用的 HTTP 协议版本;
2.状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
3.原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。
例如:
HTTP/1.1 200 OK
意思就是:“浏览器你好,我已经处理完了你的请求,这个报文使用的协议版本号是 1.1,状态码是 200,一切 OK。”
头部字段
请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头
不过使用头字段需要注意下面几点:
1.字段名不区分大小写,例如“Host”也可以写成“host”,但首字母大写的可读性更好;
2.字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”。例如,“test-name”是合法的字段名,而“test name”“test_name”是不正确的字段名;
3.字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格;
4.字段的顺序是没有意义的,可以任意排列不影响语义;
5.字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。
常用头字段
HTTP 协议规定了非常多的头部字段,实现各种各样的功能,但基本上可以分为四大类:
1.通用字段:在请求头和响应头里都可以出现;
2.请求字段:仅能出现在请求头里,进一步说明请求信息或者额外的附加条件;
3.响应字段:仅能出现在响应头里,补充说明响应报文的信息;
4.实体字段:它实际上属于通用字段,但专门描述 body 的额外信息。
首先要说的是 Host 字段,它属于请求字段,只能出现在请求头里,它同时也是唯一一个 HTTP/1.1 规范里要求必须出现的字段,也就是说,如果请求头里没有 Host,那这就是一个错误的报文。
User-Agent 是请求字段,只出现在请求头里。它使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的页面。
不过有的比较“诚实”的爬虫会在 User-Agent 里用“spider”标明自己是爬虫,所以可以利用这个字段实现简单的反爬虫策略。
实体字段里要说的一个是 Content-Length,它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定长的,需要使用 chunked 方式分段传输。小结