今天,我们通过介绍一次网络请求的完整生命周期来看看在计算机网络世界中到底使用了哪些协议。为了方便理解,我们还是对照上一篇提供的 TCP/IP 分层和协议图来看:
从客户端发起请求的时候,网络数据流是从上而下的。
以 Laravel为例,要访问这个网站,首先需要在浏览器中输入 https://laravelacademy.org,这只是网站的域名,浏览器并不知道要去哪里访问对应的资源,这个时候就需要用到 DNS 协议对域名进行解析,在域名注册商(如阿里云旗下的万网)那里绑定了域名与对应的服务器IP地址,所以 DNS 就可以通过域名获取到与之绑定的 IP 地址,比如 114.215.241.29,IP 地址相当于互联网世界的「门牌号」。
知道了目标 IP 地址后,浏览器就开始打包本次请求,这里根据传输数据加密与否分为 HTTP 请求和 HTTPS 请求,使用的分别是 HTTP 协议和 HTTPS 协议,无论使用哪种协议,都要封装请求头和请求参数,以便服务器返回相应的响应:
DNS、HTTP、HTTPS 所在的层是应用层,经过应用层封装后,浏览器将应用层的包交个下一层去完成,这个过程通过 socket 编程来实现。
下一层是传输层,传输层有两种协议,一种是无连接的 UDP 协议,一种是面向连接的 TCP 协议,UDP 无需建立连接即可通信,但是不可靠,可能会丢包,TCP 需要三次握手建立连接,能够保证数据包达到目的地,但是有额外的开销,性能和速度不如 UDP。具体使用哪个协议,需要根据具体需求来定。
对于 HTTP/HTTPS 请求来说,都是基于 TCP 协议的可靠连接,TCP 协议有两个端口,一个是浏览器监听的端口(监听服务器响应),一个是服务器监听的端口(监听客户端请求,对于 HTTP 请求,通常是 80 端口,对于 HTTPS 请求,通常是 443 端口)。操作系统会根据端口来判断,将得到的包转发给哪个进程:
传输层封装完成后,浏览器将包交给操作系统的网络层,网络层的协议是 IP 协议,在这一层,会给传输层传递过来的包加上 IP 头,其中包含源 IP 地址(浏览器所在机器)、目标 IP 地址(服务器所在机器)等信息:
操作系统知道目标机器 IP 地址后,就开始根据它来寻找目标机器,如果是本机局域网内的机器,可以直接通过 IP 地址判断出来,如果是局域网之外的机器,则需要通过网关去外面的世界查找。显然,这里的目标 IP 不在本地局域网。
操作系统启动的时候,就会通过 DHCP 协议配置 IP 地址,以及默认网关的 IP 地址:192.168.1.1。操作系统会通过 ARP 协议通过 IP 地址获取网关的 MAC 地址,并将本地网关和计算机的 MAC 地址添加到 MAC 头中:
这样,操作系统就将 IP 包交给了下一层 —— 链路层,再经由网卡发送出去(客户端机器与网关之间还有物理层的线路连接)。
注:使用网卡(NIC)的情况下,MAC地址会被烧到ROM中,任何一张网卡的MAC地址都是全球唯一的。有人可能会问,有了IP地址为什么还要MAC地址?MAC地址就相当于你的身份证号,一旦出生就写死了,一辈子都不会变,而且具有全局唯一性。IP地址可以动态分配,不能保证全局唯一,只能保证局域网内唯一,相当于你的居住地址,可能过段时间就会变。
网关收到包以后,会根据自己的知识判断下一步怎么走,网关往往是一个路由器,到某个目标 IP 地址怎么走,有一个路由表。网络请求包往往需要经过多个网关的跳转,才能达到最终的目标机器。
假设网络包经过多个网关之后,最终到达了目标服务器所在的网关,通过 ARP 协议,目标服务器根据目标 IP 地址返回一个 MAC 地址,表示目标服务器在此,然后网络包通过这个 MAC 地址在服务器所在局域网内找到目标机器。
服务端接收请求的时候,与客户端发送请求相反,网络流是自下而上的。
目标服务器发现与网络请求包的 MAC 地址对上了,取下MAC头,将包传递给上一层网络层,发现 IP 也对上了,就取下 IP 头,然后交给传输层。在传输层里,对于收到的每一个包,都要回复包收到了,这个回复不是此次请求的响应,仅仅是回复包已收到而已,这个回复会沿着包的来路回去。
如果过了一段时间(超时时间),客户端还是没有收到来自服务器的回复,会重新发送这个包,直到收到回复为止。同样,这个重发也不是重新发起上面那个客户端请求,而是传输层将同一个请求反复重试,对用户来说,只有一次请求。
回到目标服务器,当网络包到达传输层后,TCP头中有一个服务器监听端口号,通过这个端口号,可以找到 Laravel网站正在监听的端口,即 Nginx 中配置的 443 端口,端口对上之后,取下 TCP 头,将网络包交给应用层,开始对 HTTP/HTTPS 请求进行处理。
如果是前端资源的话,直接通过 Nginx 进行响应,如果是 PHP 动态请求的话,再由 Nginx 将请求转发给后台运行的 PHP-FPM 进程进行处理。当然如果 Nginx 做了负载均衡,以及后端服务是分布式系统或者提供了微服务的化(涉及到RPC远程调用),还有更加复杂的处理逻辑,这些我们放到后面去讲。
当后台服务处理完成后,就会返回一个 HTTPS 的响应包,告知用户请求成功,并返回响应内容,同样这个网络响应包和请求包一样,自上而下经过层层打包,顺着来路经过层层「关卡」(网关),回到发起请求的客户端,然后再经过自下而上的处理,最终在客户端浏览器显示 Laravel首页。
好了,通过了解一次网络请求的完整生命周期,你应该对网络包流转过程中网络分层和使用到的协议,乃至 Web 请求和响应的全貌有了一个更加清晰的认知,接下来,我们将各个击破,带你深入了解各个分层和协议的更多细节。