【网络通信 -- 直播】IM 学习系列 -- 网络通信协议简介(HTTP 协议 二)

【网络通信 -- 直播】IM 学习系列 -- 网络通信协议简介(HTTP 协议 二)

【1】一次 HTTP 请求过程

图示为一次 HTTP 请求的抓包数据分析

图示为浏览器与 Web 服务器通过 HTTP 协议交换过程

大致交换流程如下

1. 浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
2. 浏览器用 TCP 的三次握手与服务器建立连接;
3. 浏览器向服务器发送拼好的报文;
4. 服务器收到报文后处理请求,同样拼好报文再发给浏览器;
5. 浏览器解析报文,渲染输出页面;

图示为实际的互联网场景

【2】HTTP 报文

HTTP 协议的核心部分是其传输的报文内容;
HTTP 协议需要在实际传输的数据前附加一些头数据,HTTP 是一个“纯文本”的协议,因此头数据都是 ASCII 码的文本;
HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
起始行(start line),描述请求或响应的基本信息;
头部字段集合(header),使用 key-value 形式更详细地说明报文;
消息正文(entity),实际传输的数据,不一定是纯文本,可以是图片、视频等二进制数据;
这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”,HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”;

请求行由三部分构成:
请求方法,是一个动词,如 GET/POST,表示对资源的操作;
请求目标:通常是一个 URI,标记了请求方法要操作的资源;
版本号:表示报文使用的 HTTP 协议版本;

图示为请求行构成

状态行由三部分构成:
版本号,表示报文使用的 HTTP 协议版本;
状态码,一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
原因,作为数字状态码补充,更详细的解释文字;

图示为状态行构成

图示为请求头构成

图示为抓包获取的请求头数据

图示为响应头构成

图示为抓包获取的响应头数据

头字段需要注意点:
1. 字段名不区分大小写,但首字母大写的可读性更好;
2. 字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”;
3. 字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格;
4. 字段的顺序是没有意义的,可以任意排列不影响语义;
5. 字段原则上不能重复,除非这个字段本身的语义允许;

常用头字段
HTTP 协议规定了非常多的头部字段,实现各种各样的功能,但基本上可以分为四大类:
通用字段,在请求头和响应头里都可以出现;
请求字段,仅能出现在请求头里,进一步说明请求信息或者额外的附加条件;
响应字段,仅能出现在响应头里,补充说明响应报文的信息;
实体字段,属于通用字段,但专门描述 body 的额外信息;

Host 字段
Host 字段,属于请求字段,只能出现在请求头里,同时也是唯一一个 HTTP/1.1 规范里要求必须出现的字段,Host 字段告诉服务器这个请求应该由哪个主机来处理,当一台计算机上托管了多个虚拟主机的时候,服务器端就需要用 Host 字段来选择,有点像是一个简单的“路由重定向”;

User-Agent 字段
User-Agent 是请求字段,只出现在请求头里,使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据该字段来返回最合适此浏览器显示的页面,有的比较“诚实”的爬虫会在 User-Agent 里用“spider”标明自己是爬虫,可以利用这个字段实现简单的反爬虫策略;

Date 字段
Date 字段是一个通用字段,通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略;

Server 字段
Server 字段是响应字段,只能出现在响应头里,告诉客户端当前正在提供 Web 服务的软件名称和版本号,Server 字段不是必须要出现的,因为这会把服务器的一部分信息暴露给外界;

Content-Length 字段
Content-Length 字段是实体字段,表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度,服务器看到这个字段,就知道了后续有多少数据,可以直接接收;如果没有这个字段,那么 body 就是不定长的,需要使用 chunked 方式分段传输;

标准请求方法

目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式
GET,获取资源,可以理解为读取或者下载数据;
HEAD,获取资源的元信息;
POST,向资源提交数据,相当于写入或上传数据;
PUT,类似 POST;
DELETE,删除资源;
CONNECT,建立特殊的连接隧道;
OPTIONS,列出可对资源实行的方法;
TRACE,追踪请求 - 响应的传输路径;

GET/HEAD
GET 方法的含义是请求从服务器获取资源,这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据;GET 方法虽然基本动作比较简单,但搭配 URI 和其他头字段就能实现对资源更精细的操作;
例如,在 URI 后
使用“#”,就可以在获取页面后直接定位到某个标签所在的位置;
使用 If-Modified-Since 字段就变成了“有条件的请求”,仅当资源被修改时才会执行获取动作;
使用 Range 字段就是“范围请求”,只获取资源的一部分数据;

HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”;HEAD 方法可以看做是 GET 方法的一个“简化版”或者“轻量版”;因为它的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费;

POST/PUT
POST 和 PUT 方法向 URI 指定的资源提交数据,数据就放在报文的 body 里;
POST 的作用是向服务器提交数据,POST 表示的是“新建”“create”的含义;
PUT 的作用是向服务器提交数据,PUT 表示“修改”“update”的含义;

DELETE 方法指示服务器删除资源,因为这个动作危险性太大,所以通常服务器不会执行真正的删除操作,而是对资源做一个删除标记;
CONNECT 方法指示服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,这时 Web 服务器在中间充当了代理的角色;
OPTIONS 方法指示服务器列出可对资源实行的操作方法,在响应头的 Allow 字段里返回;
TRACE 方法多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径,存在漏洞,会泄漏网站的信息,通常禁止使用;

安全与幂等
HTTP 协议里,所谓的“安全”是指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改;
按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”;而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的;
HTTP 协议里,所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP 协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”;很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的;POST 和 PUT 的幂等性质按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的;

【3】URI

URI 的格式
URI 本质上是一个唯一地标记资源的位置或者名字的字符串,它不仅能够标记万维网的资源,也可以标记邮件系统、本地文件系统等任意资源;而“资源”既可以是存在磁盘上的静态文本、页面数据,也可以是由 Java、PHP 提供的动态服务;
URI 最常用的形式由 scheme、host:port、path 和 query 四个部分组成;

scheme “方案名”或者“协议名”,表示资源的访问协议
authority 表示资源所在的主机名
path 标记资源所在位置, path 部分必须以“/”开始
query 在 path 之后,用一个“?”开始,但不包含“?”,表示对资源附加的额外要求
query 查询参数是多个“key=value”的字符串,这些 KV 值用字符“&”连接,浏览器和服务器都可以按照这个格式把长串的查询参数解析成可理解的字典或关联数组形式

查询参数和头字段两者的形式很相近,query 是 key-value,头字段也是 key-value,它们有什么区别,
在发送请求时应该如何正确地使用它们
1. 查询参数是与 URI 关联在一起的,针对的就是资源(URI),是长期、稳定的;
头字段是与一次 HTTP 请求关联的,针对的是本次请求报文,是短期、临时的;
即两者的作用域和时效性是不同的;
2. 资源的属性用查询参数来描述,而如压缩传输或者控制缓存的时间等操作并不是资源本身固有的特性用头字段来描述

身份信息“user:passwd@”,表示登录主机时的用户名和密码
片段标识符“#fragment” 是 URI 所定位的资源内部的一个“锚点”或者说是“标签”,浏览器可以在获取资源后直接跳转到它指示的位置,片段标识符仅能由客户端使用

URI 的编码
URI 转义的规则直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”;
例如,空格被转义成“%20”,“?”被转义成“%3F”,中文、日文等则通常使用 UTF-8 编码后再转义,例如“银河”会被转义成“%E9%93%B6%E6%B2%B3”;

【4】状态码

状态码是三位数取值范围就是从 000 到 999
状态码分类
1×× 提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
2×× 成功,报文已经收到并被正确处理;
3×× 重定向,资源位置发生变动,需要客户端重新发送请求;
4×× 客户端错误,请求报文有误,服务器无法处理;
5×× 服务器错误,服务器在处理请求时内部发生了错误;

101 Switching Protocols 客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,如果服务器也同意变更协议,就会发送状态码 101;

200 OK 表示一切正常,如果是非 HEAD 请求,通常在响应头后都会有 body 数据;
204 No Content 含义与 200 OK 基本相同,但响应头后没有 body 数据;
206 Partial Content 是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分

301 Moved Permanently “永久重定向”,含义是此次请求的资源已经不存在了,需要改用改用新的 URI 再次访问;
302 Found “临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问;
304 Not Modified 用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制;它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”

400 Bad Request 表示请求报文有错误,只是一个笼统的错误;
403 Forbidden 表示服务器禁止访问资源;服务器可以在 body 里详细说明拒绝请求的原因;
404 Not Found 表示资源在本服务器上未找到;
405 Method Not Allowed 表示不允许使用某些方法操作资源;
406 Not Acceptable 表示资源无法满足客户端请求的条件;
408 Request Timeout 表示请求超时,服务器等待了过长的时间;
409 Conflict 表示多个请求发生了冲突,可以理解为多线程并发时的竞态;
413 Request Entity Too Large 表示请求报文里的 body 太大;
414 Request-URI Too Long 表示请求行里的 URI 太大;
429 Too Many Requests 表示客户端发送了太多的请求,通常是由于服务器的限连策略;
431 Request Header Fields Too Large 表示请求头某个字段或总体太大;

500 Internal Server Error 通用的错误码,服务器的错误客户端无法知道;
501 Not Implemented 表示客户端请求的功能还不支持;
502 Bad Gateway 通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的;
503 Service Unavailable 表示服务器当前很忙,暂时无法响应服务;

【5】HTTP 特点

灵活可扩展、可靠传输、应用层协议、请求 - 应答、无状态(有连接无状态,顺序发包顺序收包,按照收发的顺序管理报文);

无状态优势
1. 服务器没有“记忆能力”,便不需要额外的资源来记录状态信息,不仅实现上会简单一些,而且还能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务;
2. 无状态也表示服务器都是相同的,没有“状态”的差异,所以可以很容易地组成集群,让负载均衡把请求转发到任意一台服务器,不会因为状态不一致导致处理出错,使用“堆机器”的“笨办法”轻松实现高并发高可用;
无状态的劣势

服务器没有“记忆能力”,便无法支持需要连续多个步骤的“事务”操作;
例如电商购物,首先要登录,然后添加购物车,再下单、结算、支付,这一系列操作都需要知道用户的身份才行,但“无状态”服务器是不知道这些请求是相互关联的,每次都得问一遍身份信息,不仅麻烦,而且还增加了不必要的数据传输量;

HTTP 性能

请求 - 应答模式加剧了 HTTP 的性能问题,比如著名的“队头阻塞”(Head-of-line blocking),当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据;

参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】透视HTTP协议

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章