AI写作智能体 自主规划任务,支持联网查询和网页读取,多模态高效创作各类分析报告、商业计划、营销方案、教学内容等。 广告
url执行流程:https://www.baidu.com/userinfo/index.php?username=nick # 1 解析URL 浏览器做的第一步就是解析 URL 得到里面的参数。拆分访问协议(http/https),获得服务器域名(www.baidu.com),或得请求资源路径名(/userinfo/index.php)或得请求参数(username=nick)。将域名和需要请求的资源分离开来,从而了解需要请求的是哪个服务器,请求的是服务器上什么资源等等。 # 2 浏览器封装HTTP请求报文 解析完url后,浏览器此时就确定了请求的协议、服务器域名、请求路径、参数。接下来浏览器会根据这些内容封装成一个HTTP请求报文发出去。 ![](https://img.kancloud.cn/52/ec/52ec150a6cf6924de08abfe467580989_1612x1412.jpg) ``` 请求行 GET /userinfo/index.php HTTPS/2.0 请求头 HOST:www.baidu.com Contention:keey-alive Content-Type:application/x-www-form-urlencoded Content-Length:16 请求体 username=nick ``` # 3. DNS 域名解析获取 IP 地址 封装好 HTTP 请求报文后,在发送之前需要获取目标服务器的 IP。 浏览器会根据请求域名搜索【浏览器 DNS 缓存】,浏览器 DNS 缓存维护着一张域名与 IP 地址的对应表。 如果浏览器 DNS 缓存不存在对应域名的 IP,浏览器会搜索【操作系统的 DNS 缓存】。 如果操作系统的 DNS 缓存没有命中,操作系统就会将域名发送到【本地域名服务器】,本地域名服务器查询自己的 DNS 缓存,查找成功则返回结果。 本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行查询。 首先本地域名服务器会将域名发送到网络提供商,查询对应的IP,若没有命中则。 本地域名服务器向「根域名服务器」发起请求,根域名服务器是最高层次的,它并不会直接指明这个域名对应的 IP 地址,而是返回顶级域名服务器的地址。 本地域名服务器拿到这个「顶级域名服务器」的地址后,就向其发起请求,获取「权限域名服务器」的地址。 本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址。 本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来。 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来。 至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起来。 > DNS 使用的是 UDP 协议,也就是说上面各种请求的转发,都是基于 UDP 这个无连接协议的。 # 4 TCP 链接 得到域名 IP 后,接下来就是客户端和服务端建立通信,保证双方都具有可靠的收发能力。 刚开始服务端一直处于 LISTEN 状态,监听客户端请求。 第一次握手:客户端向服务端发送一个 SYN 请求连接的报文,和自己的序列号 seq=x。此时客户端处于 SYN_SEND 状态。等待服务端回复。 > SYN-SENT :在发送连接请求后等待匹配的连接请求 第二次握手:服务器收到客户端的 SYN 报文之后,会发送 SYN 报文作为应答。 同样也会发送一个服务端序列号 seq=y。同时会把客户端的 ISN + 1 作为确认号 ack 的值,表示已经收到了客户端发来的的 SYN 报文,希望收到的下一个数据的第一个字节的序号是 x + 1,此时服务器处于 SYN_REVD 的状态。 **注意:如果是 HTTPS,服务器将公钥以证书的形式发送到客户端,服务器端存放私钥和公钥。浏览器生成一串随机数,然后用公钥对随机数和 hash 签名进行加密,加密后发送给服务器;服务器用私钥解密,取出字符串和 hash 签名再通过私钥加密后发送给客户端。** > SYN-RECEIVED:在收到和发送一个连接请求后等待对连接请求的确认 第三次握手:客户端收到服务器端响应的 SYN 报文之后,会发送一个 ACK 报文,也是一样把服务器的 ISN + 1 作为 ack 的值,表示已经收到了服务端发来的的 SYN 报文,希望收到的下一个数据的第一个字节的序号是 y + 1,并指明此时客户端的序列号 seq = x + 1(初始为 seq = x,所以第二个报文段要 +1),此时客户端处于 Establised 状态。 服务器收到 ACK 报文之后,也处于 Establised 状态,至此,双方建立起了 TCP 连接。 **注意:如果是 HTTPS ,客户端用公钥对密文进行解密并判断是否被篡改,如果没有篡改,客户端向服务器端发出信息,协商后面的数据通讯将使用生成的随机字符串做为秘钥进行对称加密,同时通知服务器握手结束。服务器接受到信息后,响应协商的加密秘钥并通知客户端握手结束。** > ESTABLISHED:代表一个打开的连接,数据可以传送给用户 # 5. 浏览器真正发送请求 TCP 三次握手后,浏览器与目标服务器之间就建立了一个可靠的虚拟通道。 需要注意的是,HTTP 请求报文或者响应报文在 TCP 连接通道上进行传输的时候,由于这些报文比较大,为了更容易和准确可靠的传输【TCP 会将 HTTP 报文按序号分割成若干报文段并加上 TCP 首部,分别进行传输。接收方在收到这些报文段后,按照序号以原来的顺序重组 HTTP 报文】。 # 6. 负责传输的 IP 协议 > TCP 在三次握手、四次挥手、以及连接建立过程中的收发数据(TCP 报文段)各各阶段都是通过 IP 协议进行传输的,IP 协议将这些阶段的数据添加 IP 首部封装成 IP 数据报再进行传输。 将请求包打上IP和MAC地址。发送方IP 目标方IP。 IP 协议的作用是把各种数据包传送给对方,而且要确保确实是发送到了对应IP的服务器。期间需要满足两个必要条件 IP 地址 和 MAC 地址。 MAC 地址也是用来唯一标识一个接入互联网的设备的。 > 为什么有了IP还要MAC地址呢? > 通信的双方在同一局域网内的情况是很少见的,通常是需要多台计算机和网络设备的中转才能连接到对方。而在进行中转时,就需要利用下一站中转设备的 MAC 地址来搜索下一个中转目标 网络层指定了从哪个主机(源 IP 地址)发送到哪个主机(目的 IP 地址)。源 IP 地址和目标 IP 地址在传输过程中是不会变化的。 而数据链路层则是根据 MAC 地址在一个接一个的区间中进行传输的,每个区间内的出发地址即源 MAC 地址,每个区间内的目的地址即目的 MAC 地址。显然,随着数据的传输,源 MAC 地址和目的 MAC 地址会不断的发生变化。 将 IP 地址转化为 MAC 地址,从而在数据链路层精确的传输数据的协议就是ARP 协议。 ARP 是借助 ARP 请求与 ARP 响应两种类型的包确定 MAC 地址的。并且每个主机都有一个ARP 高速缓存 。里面有本局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射表。 # 7 物理层数据传输 物理层就是平时说的光纤、电缆这一系列物理设备。利用物理媒体以比特形式传送数据。 它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。 # 8 Nginx 处理请求 Linux 通过四次协议会逐层解包,将最后的HTTP报文发送给Nginx Master。Nginx Master 进程收到请求后会根据轮训算法发送给对应的子进程。 Nginx 子进程收到请求后,匹配对应的server块。如果 server listen 配置了backlog,并且 php-fast-cgi 处理繁忙,Nginx 就会将请求放到已连接但未进行accept处理的SOCKET队列。等待php处理。 Nginx 根据请求路径,匹配对应的location。 如果Nginx和php链接方式是 TCP 方式,则会进行TCP握手,发送数据。 如果是unix方式,则会通过socket流发送请求到php-fpm进程管理器。 # 9 php-fpm 处理请求 php-fast-cgi 监听9000端口,收到Nginx请求后,fpm通过sapi接口与php进程交互,将对应的请求分发给空闲的worker进行处理。 fpm 启动会调用各扩展的MINT方法,进行一些数据初始化(长驻内存)。然后会执行RINT对单个请求行一个初始化,接着执行php脚本。 # 10 phpcgi执行php脚本 php 通过词法分析器将php代码转换为可识别的Token标识。token 通过语法分析器转换为AST抽象语法树,抽象语法树转换为 opcodes,php Zend 引擎解释执行opcodes。 请求处理完成(Request Shutdown),按顺序调用各个模块的 RSHUTDOWN 方法,对每个变量调用 unset函数,如 unset $_SESSION 变量。 关闭模块(Module Shutdown) , PHP 调用每个扩展的 MSHUTDOWN 方法,这是各个模块最后一次释放内存的机会。这意味着没有下一个请求了。 PHP 通过 SAPI 接口将处理结果发送到Nginx。 > 这里还差mysql请求,缓存读取 # 11 服务器响应请求 浏览器的 HTTP 请求报文通过 TCP 三次握手建立的连接通道被切分成若干报文段分别发送给服务器,服务器在收到这些报文段后,按照序号以原来的顺序重组 HTTP 请求报文。然后处理并返回一个 HTTP 响应。当然,HTTP 响应报文也要经过和 HTTP 请求报文一样的过程。 # 12. 断开 TCP 连接(如果长连接还会持续发心跳包) 刚开始双方都处于ESTABLISHED 状态,假设是客户端先发起关闭请求。 第一次挥手:客户端发送一个 FIN 报文(请求连接终止:FIN = 1),报文中会指定一个序列号 seq = u。并停止再发送数据,主动关闭 TCP 连接。此时客户端处于 FIN_WAIT1 状态,等待服务端的确认。 FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认; 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。 CLOSE-WAIT - 等待从本地用户发来的连接中断请求; 此时的 TCP 处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待 2)状态,等待服务端发出的连接释放报文段。 FIN-WAIT-2 - 从远程TCP等待连接中断请求; 第三次挥手:如果服务端也想断开连接了(没有要向客户端发出的数据),和客户端的第一次挥手一样,发送 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态,等待客户端的确认。 LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认; 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答(ack = w+1),且把服务端的序列值 +1 作为自己 ACK 报文的序号值(seq=u+1),此时客户端处于 TIME_WAIT (时间等待)状态。 ``` keepalive_timeout 120s; #客户端链接超时时间。为0的时候禁用长连接。 keepalive_requests 10000; #在一个长连接上可以服务的最大请求数目。 #当达到最大请求数目且所有已有请求结束后,连接被关闭。 #默认值为100 ``` # 13. 浏览器显示界面 浏览器接收到服务器返回的 HTTP 响应报文后,根据浏览器的渲染机制对相应的数据进行渲染。 首先解析html文档,生成hrml节点,构建dom树。 在阶段一中,如果遇到css(内嵌在html文档或者外链或者内联样式都一样),则会解析css文档,生成cssom。 阶段一和阶段二都是可以并行的,等到dom和cssom准备好,会进行合并,生成render tree。 根据render tree进行layout。绘制到显示区域。 以上五个步骤并不一定一次性顺序完成,比如DOM或CSSOM被修改时,会重复执行。 在分块传输的时候,以上步骤是可以多次进行的。直到接收到`</body></html>`闭合标签。