本文是关于 HTTP 协议的学习笔记,这里做个总结与分享,有不足之处还望斧正~
网络分层
从主机 A 向主机 B 传输数据的过程中,可能会经历下图所示这些过程:
可以看出这是一个复杂的过程,于是就有了网络分层的概念,下面节选百度百科的部分解释:
网络分层就是将网络节点所要完成的数据的发送或转发、打包或拆包,控制信息的加载或拆出等工作,分别由不同的硬件和软件模块去完成。这样可以将往来通信和网络互连这一复杂的问题变得较为简单。
为了简化网络的复杂度,网络通信的不同方面被分解为多层次结构,每一层至只与紧挨着的上层或者下层进行交互,将网络分层,这样就可以修改甚至是替换某一层的软件,只要层与层之间的接口保持不变,就不会影响到其它层。
两种协议栈
网络层次可划分为两种网协议栈:
- OSI
- TCP/IP 协议簇
一个 http 请求的分层解析流程
当我们在浏览器地址栏输入一个想要访问的页面的域名,按下回车键后发生了哪些事呢?
浏览器是不认域名的,认的是 ip 地址,所以首先需要解析域名为 ip 地址,那么就看看是否缓存有对应域名的相关 DNS 信息,如果有就能直接获得 ip 地址。如果没有,就去本地 host 文件里找,如果也没有,就会发起一个 DNS 请求,获取服务器 ip 地址。
获取服务器 ip 地址的过程:
- 应用层构造一个 DNS 请求报文
- 调用传输层的 UDP 相关的协议,在 DNS 请求报文的基础上加一个 UDP 的请求头,交给网络层
- 网络层在 UDP 的请求报文基础上加一个 ip 的请求头,再把 ip 请求报文交给数据链路层
- 数据链路层会加上自己的 MAC 头和下一个机器的 MAC 地址,继续传给物理层,通常是传到路由器上面,路由器是一个三层的设备:
(1). 通过物理层进行连接,之后把数据交给数据链路层
(2). 链路层检查下 MAC 地址是不是给自己的,是就解析,不是就丢弃,解析完后,数据报文再往上传输给网络层
(3). 网络层会去看下这个数据应该传到下一个路由器的地址是多少,然后通过运营商的网络接口传到运营商的路由器上面
(4). 如果电脑配置的是运营商的 DNS 的话,就直接去运营商的 DNS 服务器上找对应的域名的 ip 地址,然后开始一层层原路返回
应用层拿到了 ip 地址后,就会进行 HTTP 请求报文的发送
- 调用传输层的 TCP 协议
- 调网络层的 ip 协议,加上 ip 头
- 调用数据链路层,加上 MAC 头
- 通过物理层和路由器进行数据的传输,这一次携带的是 ip 地址,所以不用访问运营商的 DNS 服务器,而是运营商根据 ip 地址把数据报文传输给目标服务器的运营商
- 在服务器的网络环境下,仍然逐层解析
- 物理层发往数据链路层
- 链路层判断数据是不是给自己的,是就进行解析,然后发往网络层
- 网络层判断 ip 地址是不是自己,是就进行解析,然后发往传输层
- 传输层解析 TCP 的端口比如80,把请求报文交给应用层应用程序
- 应用层解析报文,构造一个 HTTP 的响应报文,逐层返回给客户端
HTTP 协议
超文本传输协议(HTTP)是一种无状态的,以请求/应答方式运行的协议,它使用可扩展的语义和自描述消息格式,与基于网络的超文本信息系统灵活的互动。之所以说是无状态,下面有一段摘自知乎的解释:
因为 HTTP 的每个请求都是完全独立的,每个请求包含了处理这个请求所需的完整的数据,发送请求不涉及到状态变更。至于 HTTP/2,它应该算是一个有状态的协议了(有握手和 GOAWAY 消息,有类似于 TCP 的流控),所以以后说“HTTP 是无状态的协议”就不太对了,最好说“HTTP 1.x 是无状态的协议”
HTTP 报文形式
HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
- 起始行(start line)
- 描述请求或响应的基本信息
- 请求 GET /api/app/warehouseArea HTTP/1.1
- 响应 HTTP/1.1 200 OK
- 头部字段集合(header)
- 使用 key-value 的形式更详细地说明报文,例如,Connection: Keep-Alive
- 字段名不区分大小写,字段名里不能出现空格,可以使用连字符-,不能使用下划线
_
,字段名后面紧接冒号,不能有空格,而冒号后的字段值前可以有多个空格 - 字段的顺序可任意排列
- 字段原则上不能重复,除非这个字段本身语义允许,例如 Set-Cookie
- 消息正文(entit)
- 实际传输的数据,不一定是纯文本,可以是图片、视频等二进制数据
HTTP 请求的完整过程
TCP(传输控制协议)
面向连接的,可靠的,基于字节流的传输层通信协议。
特点
- 基于连接的:数据传输前需要建立连接
- 全双工的:双向传输
- 字节流:不限制数据大小,打包成报文段,保证有序接收,重复报文自动丢弃
- 流量缓冲:解决双方处理能力的不匹配
- 可靠的传输服务:保证可达,丢包时通过重发机制实现可靠性
- 拥塞控制:防止网络出现恶性拥塞
TCP 连接管理
TCP 连接:四元组[源地址,源端口,目的地址,目的端口]
确立连接:TCP 三次握手
- 所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送 3 个包。
- 同步通信双方初始序列号(ISN)
- 协商 TCP 通信参数(MSS,窗口信息,指定校验和算法)
TCP 包
如何进行握手(三次握手)
说明:
- SYN_SENT 表示请求连接,当你要访问其它的计算机的服务时首先要发个同步信号给该端口,此时状态为SYN_SENT,如果连接成功了就变为 ESTABLISHED
- "TCB" 是 "TCP (Transmission Control Protocol) Control Block" 的缩写,意思是 "TCP(传输控制协议)控制块"
TCP 四次挥手
A:发送 FIN 数据包,代表 A 不再发送数据
B:收到请求,开始应答,避免 A 重新发送 FIN
B:处理完数据后关闭连接,发送 FIN 请求
A:收到请求后发送 ACK 应答,B 服务可以释放连接
2MSL
MSL 是 Maximum Segment Lifetime 英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
等待 2MSL 后释放连接,可以
- 防止报文丢失,导致 B 重复发送 FIN
- 防止滞留在网络中的报文对新建立的链接造成数据扰乱
HTTPS
由于 HTTP 天生“明文”的特点,整个传输过程完全透明,任何人都能在链路中截获、修改或者伪造请求/响应报文,数据不具有可信性。因此诞生了为安全而生的 HTTPS 协议。 使用 HTTPS 时,所有的 HTTP 请求和响应在发送到网络之前,都有进行加密。
One More Thing
上面有说道 HTTP 协议的请求报文和响应报文,请求头 header 参数字段名不能使用划线 _
,但其实是是合法的、符合 HTTP 标准的,服务器默认禁止使用是因为 CGI 历史遗留问题:下划线和中划线都为会被映射为 CGI 系统变量名中的下划线,这样容易引起混淆。在 nginx 服务器中,通过设置 underscores_in_headers on
是可以在字段名中使用下划线的。