HTTP 请求,是客户端和服务器之间进行数据传输的方式之一,是客户端向服务器发送请求消息,用于请求特定的资源或执行特定操作。今天,我们来揭秘一下 HTTP 请求的过程。
当我们想要发起一个 HTTP GET 请求时,需要指定好请求的 URL(juejin.cn/pins) 和请求参数。系统收到我们的 HTTP 请求时,要先进行 HTTP 解析。
HTTP 解析
HTTP 解析主要是系统将用户请求或响应的消息进行解析和处理,如解析 URL,生成请求报文或响应报文等。
URL 的结构比较简单,主要由数据使用协议、服务器名称和数据源路径三个部分组成,如下图所示:
以 juejin.cn/pins 为例:
- 开头的 "https" 表示我们向服务器请求数据使用的协议,这里使用的 https 协议来向服务器请求数据。"//" 后跟着的字符串表示服务器的域名,如 juejin.cn 。
- /pins 是数据源的路径名,表示资源位于服务器的相对"根目录"的位置。这里的"根目录"并不是我们理解的系统根目录:"/"。通常情况下,我们会在为 Web 服务器里配置"根目录",这个"根目录"可以是系统下的 var/www/html/ 类似这样的目录。
- 若我们在请求的时候不指定数据源的路径,一般默认会去获取默认的资源文件,如 index.html 或者 default.html。
解析 URL 后,我们就可以确定请求的服务器和请求的资源,根据这些信息可以生成 HTTP 请求报文,格式如下图所示:
- 方法,HTTP 请求可以使用 9 种方法,这些方法主要是用来约束客户端和服务器之间进行数据传递的行为和操作,其中 GET 和 POST 是我们最常用的方法:
- GET:从服务器获取资源;
- POST:向服务器提交数据,常用于提交表单数据、上传文件等操作;
- PUT:将数据发送到服务器,常用于创建或替代资源;
- DELETE:从服务器删除指定的资源;
- PATCH:用于对资源进行部分更新,只更新请求中指定的字段或属性;
- HEAD:与 GET 方法类似,但只获取响应头信息,不返回响应体,常用于检查资源是否存在或获取资源的元数据;
- OPTIONS:获取服务器支持的请求方法列表、服务器配置信息等;
- TRACE:用于回显服务器收到的请求,用于测试或诊断;
- CONNECT:将请求连接转换为透明的 TCP/IP 通道,通常用于进行加密隧道的建立,例如 HTTPS 。
- 版本,用来表示客户端和服务器之间的传输数据的协议,HTTP 目前有 4 个版本,分别是 HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/3
- 首部字段名和字段值,用来传递关于请求或响应的附加信息,以键值对的形式出现,提供关于请求和响应的元数据和相关数据,主要分为通用首部字段、请求首部字段和响应首部字段。
除了 HTTP 请求报文外,还有 HTTP 响应报文,是由服务端发送给客户端的消息,如下图所示:
- 状态码,服务器响应客户端的请求时返回的三位数子代码,用于表示请求的处理结果,主要有 5 大类;
- 短语,短语和状态码是关联的,用于提供更详细的描述和说明。
组装好 HTTP 请求报文后,应用层会将数据下传给传输层,在这期间需要获取服务器域名的真实地址------DNS真实地址查询。
DNS------真实地址查询
DNS(Domain Name System)是互联网中用于将域名转换为对应 IP 地址的分布式命名系统。它充当了一个类似电话簿的角色,将易记的域名映射到计算机能够理解的 IP 地址。
DNS使用分层、分布式的架构,由许多相互连接的DNS服务器组成。以 juejin.cn 为例,越往右层级越高。DNS 的层级结构类似于树状结构,如下图所示:
- 根域 DNS 服务器是最顶级的服务器,它主要是管理顶级域名的解析,主要作用是提供对顶级域名服务器的递归查询和指引,任何 DNS 服务器都存储了根据 DNS 服务器的地址。
- 除了图中所述的 DNS 服务器外,还有本地 DNS 服务器,位于用户本地网络中的 DNS 服务器,为用户设备(如计算机、手机、路由器等)提供域名到 IP 地址的转换功能。
DNS 域名解析的工作流程如下,以 juejin.cn 为例:
- 将域名先发送给本地 DNS 服务器(也就是 TCP/IP 中设置的 DNS 服务器地址),询问该域名的 IP 地址;
- 本地域名 DNS 服务器收到客户端请求后,若自己缓存该域名的 IP 地址,则直接返回,若没有则去查询根域名 DNS 服务器,根域名 DNS 服务器不做域名解析,而是告诉下一层级的 DNS 服务器地址,这里会告诉 .cn 顶级 DNS 服务器地址;
- 本地 DNS 服务器收到顶级 DNS 服务器地址后,发起请求查询域名对应的 IP 地址;
- 顶级 DNS 服务器收到请求后,提供了 juejin.cn 区域的权威 DNS 服务器地址;
- 本地 DNS 收到后向权威 DNS 服务器地址后,发起请求;
- 权威 DNS 服务器查询后将对应的 IP 地址告诉本地 DNS 服务器;
- 本地 DNS 服务器再将 IP 地址返回客户端,让客户端继续接下来的流程。
通过 DNS 拿到服务器域名对应的 IP 后,由于 HTTP 是基于 TCP 传输的,接下去就由 TCP 协议进行封装传输。
TCP------可靠传输
TCP 协议全称是传输控制协议,是一个可靠、按需和面向连接的传输协议。TCP 具备很多优秀的特性,如超时重传、流量控制和拥塞控制等。TCP 表面上只是负责接受应用层的委托进行收发数据,实际上由网络层进行实际的网络包收发操作。
TCP 协议也有自己的格式,如下图所示:
-
源端口和目的端口,用于标识不同的应用程序和服务,端口号是一个 16 位的数字,从 0 ~ 65535,0 ~ 1023 是已经被用于一些标准服务了;
-
序号,32 位的无符号整数,用于确保数据的可靠传输和报文段的排序;
-
确认号,32 位的无符号整数,用于标识接收方期望从发送方接收的下一个字节的序号。接收方收到数据后,会将确认号字段设置为已成功接收的最后一个字节的序号加 1,通过确认号可以实现可靠的数据传输和流量控制;
-
状态位:用于指定数据段的不同状态和执行特定的操作,有 6 个标志位
- URG:表示紧急指针字段的值是否有效,当为 1 时,数据是紧急数据,需要被优先处理;
- ACK:表示确认号的字段是否有效,当为 1 时,表示确认号字段有效;
- PSH:表示接收方应该立即将数据推送给应用层,而不是等待缓冲区填满再推送;
- RST:表示重置连接,当 RST 位被设置为 1 时,表示连接出错或被强制中断,需要终止连接
- SYN:表示建立连接请求,当为 1 时,表示发送方请求建立连接,并制定初始化序号;
- FIN:表示发送方已完成数据发送,请求关闭连接。
-
窗口大小:16 位无符号整数,指的是接收方的缓冲区大小,用于控制发送方发送速率,实现流量控制。
TCP 会将 HTTP 的报文作为数据体。在实际传输过程中,网络层 IP 协议对数据包的传输大小有限制,因此,TCP 也会对数据体的大小做限制,若 HTTP 报文过大,会对其进行分段,如下图所示:
一般情况下,网络层规定数据包的大小不能超过 MTU,MTU 一般是 1500 个字节。而 MSS 一般是 MTU 去除 IP 和 TCP 首部之后,一个网络包能容纳的 TCP 数据的最大长度,如下图所示:
TCP 在传输数据前,需要同服务器进行三次握手后,才能建立连接,如下图所所示:
- 一开始,客户端和服务端都处于 CLOSE 状态,服务端会主动监听某个端口,处于 LISTEN 状态;
- 客户端主动发起 SYN,建立连接,之后处于 SYN-SEND 状态;
- 服务端收到客户端的 SYN,也返回 SYN,并且对客户端的 SYN 进行 ACK,之后处于 SYN-RCVD 状态
- 客户端收到服务端发送的 SYN 和 ACK 之后,发送对 SYN 确定的 ACK,之后处于 ESTABLISHED;
- 服务端收到 ACK 的 ACK 之后,也处于 ESTABLISHED;
- 通过三次握手,保证客户端和服务端都有发送和接收的能力。
当客户端和服务端建立连接后,TCP 将 HTTP 报文当做数据部分,加上首部便封装成 TCP 数据段,然后就向下交付给网络层的 IP 协议处理。
IP------远程定位
IP 协议是网络层中用于数据传输的协议,传输层 TCP 协议在执行连接、收发、断开等操作,实际上都是委托 IP 协议去执行的。
IP 协议有自己的协议格式,如下图所示:
-
版本:IP 协议的版本,目前主要有 IPv4 和 IPv6;
-
协议:用于指示封装在 IP 数据包中的上层协议,占 8 位,0~255,常用的协议有
- ICMP(1):用于在IP网络中传递错误消息和操作控制消息。
- IGMP(2):用于在IP网络中进行多播组成员管理。
- TCP(6):提供可靠的、面向连接的数据传输服务。
- UDP(17):提供无连接的、不可靠的数据传输服务。
- IPv6(41):用于封装IPv6数据报。
- OSPF(89):用于在路由器之间动态确定最佳路径的内部网关协议。
- SCTP(132):提供面向消息的可靠传输服务。
-
原地址 IP 和目标地址 IP:用于指示数据报的目的地和源头
- 原地址 IP 即客户端输出的 IP 地址
- 目标地址即通过 DNS 域名解析得到的 Web 服务器 IP
IP 协议将上层的 TCP 数据段当做数据体,加上 IP 首部,封装成一个数据包。想要将数据包传输给目的 IP 地址,这需要使用上 IP 协议的寻址能力。
在 IPv4 协议中,IP 地址共 32 位,由网络号和主机号组成。其中,网络号用于表示该 IP 地址属于哪一个子网,主机号用于表示 IP 地址属于该子网下的哪一个主机。
举个例子,比如我们需要对 IP 地址为 10.100.122.2/24 的主机发送一份数据,24 对应的子网掩码为 255.255.255.0。
第一步我们需要找到 IP 地址对应的子网,将 10.100.122.2 和 255.255.255.0 进行 AND 运算,得出子网号为 10.100.122.0,如下图所示:
第二步,子网掩码取反后为 0.0.0.255,将 10.100.122.2 和 0.0.0.255 进行 AND 运算,得出主机号为 0.0.0.2。因此,我们便能将数据数据给 10.100.122.0 子网下的 0.0.0.2 主机。
除了寻址功能,网络层还需要 IP 协议提供路由选择的能力。路由器需要通过选择找到目标地址的子网,进而将数据包转发到对应的网路中。
网络层将数据包封装好后,会接着向下传输给网络连接层,让网络连接层给数据包写上 MAC 地址,以便在物理链路中传输。
MAC------两点传输
MAC(Media Access Control)地址是网络设备(如网卡)在物理层上的唯一标识符。它是一个由48位二进制数(通常以十六进制表示)组成的地址,用于在局域网中唯一标识每个网络接口。
MAC 的首部格式如下图所示:
- MAC 头部包含发送方的 MAC 地址和接收方目标 MAC 地址,用于两点之间的传输
- MAC 包头的协议类型一般只使用 IP 协议(0000)和 ARP 协议(0806)
一般在设备出厂的时候,发送方网卡的 MAC 地址是生产的时候就写到 ROM 中。而接收方的 MAC 地址则需要通过 ARP 协议,通过广播的方式在子网中查找目标 IP 地址对应的 MAC 地址。
在网络连接层中,数据包写上 MAC 地址后,接下来通过网卡向外出口数据包。
网卡------出口
经过前面的封装,我们的数据包此时只是一堆数字信息,需要将其转换为电信号才能在物理链路上进行网络传输。网卡会负责将数字信号转换为电信号。
网卡驱动获取到数据包后,会将其复制到网卡内的缓存区,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列,如下图所示:
- 起始帧分界符是一个用来表示包起始位置的标识
- 末尾的 FCS 用来检查包传输过程中是否有损坏
网卡将数据转换为电信号后往外交付,需要经过交换机或路由器才能传输到服务器。
交换机------局域网内通信
交换机(Switch)是计算机网络中的网络设备,用于在局域网(LAN)中连接多个设备并转发数据包。它在数据连接层工作,被称为二层网络设备(只有链路层和物理层)。
交换机的主要作用有:
- 数据包转发;
- 广播和组播处理;
- 网络隔离和分割;
- 带宽管理。
交换机收到网卡发送过来的电信号,会进行如下操作:
- 电信号到达交换机接口,交换机将电信号转为数字信号;
- 通过帧尾部的 FCS 校验错误,若没有错误则放到缓存区(这个过程和网卡相同);
- 计算机的网卡接收到包的时候需要检查下接收方 MAC 地址是不是自己,如果不是则丢弃;交换机的端口不核对接收方 MAC 地址,全都放到缓冲区;
- 接着需要查询下这个包接收方的 MAC 地址是否已经在自己的地址表中,地址表主要包含两个信息:设备的 MAC 地址和对应的端口。
如果找不到指定的 MAC 地址后,一般是该地址的设备没有向交换机发送过数据帧或这个设备有一段时间没工作导致地址被删除。交换机会进行以下处理:
- 交换机会将数据帧转发到除源端口之外的所有端口,只有相应的接收者才接收包,其他设备会忽略包
- 若有目标设备做出响应,交换机会将它的地址写入到 MAC 表中进行缓存
- 接收方的 MAC 地址是一个广播地址(FF:FF:FF:FF:FF:FF/255.255.255.255),交换机会将包发送到除端口之外的所有端口
路由器------跨网络通信
在网络传输中,路由器是关键的设备,它在实现局域网内通信的同时也负责连接到互联网。
路由器和交换机的都具备转发下一个路由器或目标设备的功能。但路由器是基于 IP 设计的,称为三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;而交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具备 MAC 地址。
路由器的基本原理:
- 路由器具有 MAC 地址,能够成为以太网的发送方和接收方,同时也具有 IP 地址,基本上和网卡一样;
- 路由器会接收发给自己的以太网包,然后路由表查询转发目标,再由相应端口作为发送方将以太包发出去。
路由器的接收数据包的过程:
- 电信号到达路由器后,路由器中的模块会将电信号转换为数字信号,通过包末尾的 FCS 进行错误校验;
- 检查 MAC 头部的接收方 MAC 地址,如果是发送给自己的就放到缓冲区中,否则就丢弃这个包。
通过路由器和交换机,服务端可以接收到客户端发送的数据包,并逐层开始解开数据包。
服务端和客户端解数据
数据包抵达服务器后,服务器会先解开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就将包收起来。
接着继续解开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。解开 TCP 的头,里面有序列号,需要看一看这个序列包是不是服务器想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP 头部里面还有端口号, HTTP 的服务器正在监听这个端口号。
于是,服务器就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。
服务器的 HTTP 进程看到,知道请求是要访问一个页面,就把这个网页封装在 HTTP 响应报文里。HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。
数据包封装好后,从网卡出去,交由交换机转发到出城的路由器,路由器就把响应数据包发到了下一个路由器,经过若干个路由器后。最后跳到了客户端的路由器,路由器解开 IP 头部发现是发送给自己的,于是又把包发给了交换机,再由交换机转发到客户端。
客户端收到了服务器的响应数据包后,进行解包,便拿到服务端返回的数据。
最后,客户端和服务器发起了 TCP 四次挥手,至此双方的连接就断开了。
总结
总而言之,当客户端发起一个 HTTP 请求时,数据包会被 TCP/IP 协议从上到下逐层处理和封装后,才会被送到网卡缓存中,随后由交换机和路由器向外通信。
当服务端接收到客户端的数据包时,会从下而上的解封装和处理,最后交付到应用层中。