计算机网络-浏览器输入url地址到显示主页的过程 - 个人文章 - SegmentFault 思否

188 阅读11分钟
原文链接: segmentfault.com

浏览器输入url地址到显示主页的过程


​ 不得不说这是面试里的一位常客,我们可以把这整个构成分成DNS解析、TCP连接、发送HTTP请求、服务器处理请求并返回HTTP报文、浏览器解析渲染页面、连接结束这六个阶段,接下来详细讲解一下这几个阶段:
​ 其实开始之前,还有几个过程:那就是浏览器要对用户输入的网址做初步的格式化检查(比如baidu@.com1是非法无效的),只有通过了这一步才会继续,还有就是浏览器会首先查询自身缓存,如果有的话直接返回,没有的话继续下面的过程:

DNS解析

​ 拿百度为例,当你输入www.baidu.com的时候,计算机并不知道它在哪,而是需要知道ip地址才可以进行请求,也就是说,我们要做的第一步就是如何通过url地址来获得目的ip地址。
那有些人就有疑问了:既然这么麻烦,那为什么我们要使用域名而不是直接使用ip地址,这是因为

  • IP地址不容易记住,特定的域名,更加方便用户记忆或辨识
  • IP地址与域名并不是一对一的映射关系,一个域名可以对应多台设备的IP地址,一个IP地址只能对应一个域名

​ 这里引入一个概念叫做DNS解析,DNS解析就是为了将网址转变为相应的ip地址,其中DNS解析是一个递归查询的过程:

  • 首先,本机先通过DNS服务器在DNS高速缓存本地硬盘里的host文件查询是否有www.baidu.com的ip地址,如果查到直接将结果返回给计算机,结束。反之,如果没有找到,继续下一步。
这里插一句:访问www.github.com的速度很慢的原因是github服务器在国外因此会被墙,这个时候可以尝试修改本地host文件在最后加上 域名 + ip地址,这样的话可以缩短访问时间。
  • 本地域名服务器继续询问根域名服务器(.),根域名服务器返回对应的顶级域名服务器(com)地址给本地域名服务器返回
  • 本地域名服务器根据顶级域名服务器地址访问顶级域名服务器(com),顶级域名服务器将baidu.com的地址返回给本地域名服务器
  • 本地域名服务器根据baidu.com域名服务器的地址找到baidu.com域名服务器,询问www.baidu.com的IP,并得到返回结果
  • 本地域名服务器将得到的结果返回给本机,同时将得到的地址保存在DNS高速缓存一段时间,避免下次访问还会经历很多步骤。
  • 本地域名服务器将得到www.baidu.com的地址给浏览器,同时浏览器会对www.baidu.com进行缓存,方便下次访问。

关于DNS,这里继续讨论一下关于DNS优化的问题:

  • DNS缓存,避免DNS解析(涉及到UDP和TCP请求)中进行太多的查找,所以DNS中存在着多级缓存,包括浏览器缓存、系统缓存、路由器缓存、根域名服务器缓存、顶级域名服务器缓存等等
  • DNS负载均衡,又叫DNS重定向,我们知道一个网站背后可能有很多机器,DNS服务器会返回给用户距离他最近的点的IP地址

​ 浏览器拿到DNS服务器传输回来的ip地址之后,会把ip地址打到协议上,同时请求参数也会在协议搭载,然后发给对应的服务器,HTTP请求分成三个阶段:TCP连接、HTTP请求相应信息、TCP断开连接

TCP连接

TCP三次握手的过程

TCP三次握手.png
​ 最初,Client和Server都处于CLOSED(关闭)状态,本例中Client主动打开连接,Server被动打开。Server的TCP服务器首先创建传输控制块TCB,准备接受客户端进程的连接请求,然后Server就处于LISTEN(监听)状态,等待客户端的连接请求。
​ 第一次(红色表示):Client首先创建传输控制块TCB,再打算建立TCP连接时,向B发出连接请求报文段,客户端给服务器端发送一个SYN = 1(同步序列号)和一个初始seq序列号。不能携带数据
​ 第二次(蓝色标识):服务器端返回SYN/ACK标识符,以及Seq和ack,不能携带数据
​ 第三次(绿色标识):客户端收到Server的确认之后,给Server发送确认信息,这是ACK = 1,Seq = X+1,ack = Y + 1。这是,TCP连接已经建立,Client进入ESTABLISHED(已建立连接)状态。

为什么是三次,不是两次或者四次握手

  • 为什么不是两次
    为了防止已经失效的连接请求报文段突然又传送到了Server,因而产生错误,我们假设一种情况:Client发出的第一个连接请求报文段并没有丢失,而是在网络结点长时间的滞留了,导致它会在连接释放之后的某个时间到达Server。本来这是一个早已失效的报文段,但是Server收到此报文段后,误以为Client又发出了一次新的请求,于是就向Client发出确认报文段,同意建议连接。
    ​ 如果不采取三次握手,Server发出确认之后就默认为新的连接已经建立了,并一直等待Client发来数据,这会导致浪费Server的很多资源。
    ​ 假如采用了三次握手,由于Client实际上并没有发送建立连接的请求,所以会忽略Server发送来的确认,也不会向Server发送数据,Server由于收不到Client返回的确认信息,知道Client并没有要求建立连接
  • 为什么不是四次
    ​ 因为通过三次握手,服务端和客户端都可以相互确认对方的通信状况,都收到了确认信息,所以再次增加握手次数没有太大的意义。

为什么要传回SYN

​ SYN是TCP/IP建立连接的时候的握手信号。客户端首先向服务端发送一个SYN消息,服务器使用SYN-ACK应答表示接收到了这个信号,这之中接收端传回SYN,就是为了告诉发送端,我接收到的信息确实就是你发送的信号。

传了SYN,为什么还要传ACK

​ 这是因为双方的要建立连接必须要确定双方都能够互相发送信息,传输SYN,只能确保客户端能够正确发送消息给服务端,但是接收方 -> 发送方的通道还需要ACK来进行验证。

发送HTTP请求

​ 构建HTTP请求报文,关于HTTP请求我会放到别的文章当中来讲。

服务器处理请求并返回HTTP报文

​ 服务器接收到这个请求,并根据路径参数映射到特 定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器,这里我也放到别的文章来讲。

浏览器解析渲染页面

​ 浏览器解析并渲染视图,若遇到对 js 文件、css 文件及图片等静 态资源的引用,则重复上述步骤并向服务器请求这些资源;浏览器根据其请求到的资源、 数据渲染页面,终向用户呈现一个完整的页面。

连接结束

​ 我们知道,当数据发送完毕之后,需要断开TCP的连接,这涉及到TCP断开连接的四次挥手过程,接下来我们来关注一下TCP断开连接的四次挥手过程以及过程中遇到的问题。

TCP断开连接四次挥手过程

  • 第一次挥手: A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 把连接释放报文段首部的终止控制位 FIN 置 1,其序号 seq = u(等于前面已传 送过的数据的后一个字节的序号加 1),这时 A 进入 FIN-WAIT-1(终止等待1)状态,等 待 B 的确认。请注意:TCP 规定,FIN 报文段即使不携带数据,也将消耗掉一个序号。
  • 第二次挥手: B 收到连接释放报文段后立即发出确认,确认号是 ack = u + 1,而这个报文段自己 的序号是 v(等于 B 前面已经传送过的数据的后一个字节的序号加1),然后 B 就进入 CLOSE-WAIT(关闭等待)状态。TCP 服务端进程这时应通知高层应用进程,因而从 A 到 B 这个方向的连接就释放了,这时的 TCP 连接处于半关闭(half-close)状态,即 A 已经没 有数据要发送了,但 B 若发送数据,A 仍要接收。也就是说,从 B 到 A 这个方向的连接并 未关闭,这个状态可能会持续一段时间。A 收到来自 B 的确认后,就进入 FIN-WAIT-2(终止等待2)状态,等待 B 发出的连接释放报 文段。
  • 第三次挥手: 若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。这时 B 发出的 连接释放报文段必须使 FIN = 1。假定 B 的序号为 w(在半关闭状态,B 可能又发送了一 些数据)。B 还必须重复上次已发送过的确认号 ack = u + 1。这时 B 就进入 LASTACK(后确认)状态,等待 A 的确认。
  • 第四次挥手: A 在收到 B 的连接释放报文后,必须对此发出确认。在确认报文段中把 ACK 置 1,确 认号 ack = w + 1,而自己的序号 seq = u + 1(前面发送的 FIN 报文段要消耗一个序 号)。然后进入 TIME-WAIT(时间等待) 状态。请注意,现在 TCP 连接还没有释放掉。必 须经过时间等待计时器设置的时间 2MSL(MSL:长报文段寿命)后,A 才能进入到 CLOSED 状态,然后撤销传输控制块,结束这次 TCP 连接。当然如果 B 一收到 A 的确认就进入 CLOSED 状态,然后撤销传输控制块。所以在释放连接时,B 结束 TCP 连接的时 间要早于 A。

为什么 A 在 TIME-WAIT 状态必须等待 2MSL 的时间呢?

  • 为了保证 A 发送的最后一个 ACK 报文段能够到达 B。这个 ACK 报文段有可能丢失, 因而使处在 LAST-ACK 状态的 B 收不到对已发送的 FIN + ACK 报文段的确认。B 会超时 重传这个 FIN+ACK 报文段,而 A 就能在 2MSL 时间内(超时 + 1MSL 传输)收到这个 重传的 FIN+ACK 报文段。接着 A 重传一次确认,重新启动 2MSL 计时器。后,A 和 B 都正常进入到 CLOSED 状态。如果 A 在 TIME-WAIT 状态不等待一段时间,而是在发送完 ACK 报文段后立即释放连接,那么就无法收到 B 重传的 FIN + ACK 报文段,因而也不会 再发送一次确认报文段,这样,B 就无法按照正常步骤进入 CLOSED 状态。
  • 防止已失效的连接请求报文段出现在本连接中。A 在发送完后一个 ACK 报文段后, 再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。 这样就可以使下一个连接中不会出现这种旧的连接请求报文段。

为什么第二次和第三次不能合并,第二次和第三次之间等待的什么

​ 当服务器执行第二次挥手之后, 此时证明客户端不会再向服务端请求任何数据, 但是服 务端可能还正在给客户端发送数据(可能是客户端上一次请求的资源还没有发送完毕), 所以此时服务端会等待把之前未传输完的数据传输完毕之后再发送关闭请求。

保活计时器

​ 除时间等待计时器外,TCP 还有一个保活计时器(keepalive timer)。设想这样的 场景:客户已主动与服务器建立了 TCP 连接。但后来客户端的主机突然发生故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用保活计时器了。​ 服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两个小 时。若两个小时都没有收到客户端的数据,服务端就发送一个探测报文段,以后则每隔 75 秒钟发送一次。若连续发送 10个 探测报文段后仍然无客户端的响应,服务端就认为客户端 出了故障,接着就关闭这个连接。


本文部分内容参考自:
1.zhuanlan.zhihu.com/p/...
2.《计算机网络-自顶向下方法》