相信大家都知道,这是前端面试中的一道高频考题,借着马上要去面试的时机, 在此做一个总结。

正文开始
相信大家看到这个题目,大家心中马上就有了答案,无非就那几步。但是面试时为了更好地突出自己,我们需要对面试题进行举一反三的回答。所以我们今天就来好好学习怎么把这个问题回答的更彻底。

废话不多说,下面我们来说说我们一般的回答:

  1. 输入网址
  2. 根据输入的网址获取网址对应的IP地址
  3. 根据解析出来的IP地址找到对应的服务端,并和服务端建立连接
  4. 连接建立完成后,开始向服务端发起HTTP请求
  5. 服务端响应HTTP请求并返回处理后的数据
  6. 浏览器解析服务端返回的数据
  7. 浏览器对页面进行布局渲染

下面我们就开始一步步的细讲,由于篇幅原因,部分内容可能需要另开博客仔细讲解。

一:输入网址

这一步没什么讲的,直接跳过

二:根据输入的网址获取网址对应的IP地址

我们在向浏览器输入网址时,其实就是要向服务器请求我们想要的页面内容,但是浏览器不能识别我们所输入的网址,要将域名转换成IP地址才能够被正常的请求。将域名转换成IP地址这项工作需要DNS服务器的参与。
DNS的作用就是通过域名查找对应的IP地址。因为IP地址存在数字和英文的组合(IPv6),很不利于人类记忆,所以就出现了域名。你可以把域名看成是某个IP的别名,DNS就是去查询这个别名的真正名称。
首先我们来看一张草图:
DNS服务器递归查询和迭代查询
上图详细的介绍了DNS的解析过程。客户端收到你输入的域名地址后,比如说,当你想访问www.Google.com时,会进行如下一系列操作:

  1. 首先会去缓存中查询是否有对应IP;
  2. 如果在缓存中找不到对应的IP,那么就去系统配置的DNS配置文件中查询;
  3. 如果配置文件中也没有找到,接下来就会直接去DNS根服务器查询,此时会找出负责com这个顶级域名的服务器;
  4. 然后去该服务器查找Google这个二级域名;
  5. 接下来三级域名的查询其实是我们配置的,你可以给www这个域名配置一个IP,然后还可以给别的三级域名配置一个IP;

以上介绍的是 DNS 迭代查询和递归查询,区别就是前者是由客户端去做请求,后者是由系统配置的DNS服务器做请求,得到结果后将数据返回给客户端。
说明:正常情况下,本地DNS服务器的缓存中已经有了comDNS服务器的地址,因此请求根域名这一步不是必需的。
PS: 为什么DNS更适合用UDP?(曾经一个哥们面试时被问到)
这个问题的回答需要从TCP和UDP的特性来回答,很明显,使用UDP相比于TCP耗费的网络性能肯定要少一些,基于UDP的DNS协议只需要一次请求、一次应答即可;而基于TCP的DNS协议需要经过三次握手、四次挥手的连接过程。当然这是基于数据包的数量以及占有网络资源方面的分析,那么数据包的一致性方面呢?首先我们应该要知道的是DNS数据包都不是那种特别大的,所以使用UDP不需要分包,如果丢包就是全部丢包,如果收到了数据,就是收到了全部数据,所以只需要考虑丢包的情况,出现了丢包就重新请求一次就好了。而且DNS的报文允许填入序列号字段,对于请求报文和其对应的应答报文,这个字段是相同的,通过它可以区分DNS应答是对应的哪个请求

  • DNS通常是基于UDP的,但当数据长度大于512字节的时候,为了保证传输质量,就会使用基于TCP的实现方式

三:根据解析出来的IP地址找到对应的服务端,并和服务端建立连接

当第二步完成之后,我们会得到服务端的IP地址,这个时候就需要去和服务端建立连接了。和DNS的请求不同,客户端和服务端通过TCP建立连接,在这里我们可以简单的来说说TCP三次握手,先来看一下下面的一张图:
TCP三次握手
PS:重新画了一次TCP三次握手的图片,想不到还能画出来
废话不多说,下面我们直接来看图:

  1. 第一次握手(SYN=1; seq=x):建立连接。客户端发送连接请求报文段,将SYN置为1,sequence Number为x;然后客户端进入SYN_SENT状态,等待服务端确认。
  2. 第二次握手(ACK=1; SYN=1; Ack=x+1; seq=y):服务端收到SYN报文段。服务端收到SYN报文段,需要对SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number +1);同时自己还要发送SYN请求信息,将SYN置为1,Sequence Number为y;然后服务端将上述所有信息封装到报文段中,一并发送给客户端,服务端进入SYN_RECV状态。
  3. 第三次握手(ACK=1; Ack=y+1):客户端收到SYN+ACK报文段。然后将Acknowledgment Number置为y+1,向服务端发送ACK报文段,这段报文发送完毕后,客户端和服务端同时进入ESTABLISHED状态,完成TCP三次握手。
    完成了TCP三次握手后,客户端和服务端就可以开始传输数据了。以上是TCP三次握手的简单描述。

四:连接完成后,开始向服务端发起HTTP请求

当TCP握手结束后,如果客户端发起的是HTTP请求,那么可以直接与服务端交互;但是如果发起的是HTTPS请求,那么就还需要TLS握手才能发起请求。
HTTPS还是通过HTTP来传输信息,但是信息都是通过了TLS协议加密。
TLS协议位于传输层之上,应用层之下。首次进行TLS协议传输需要两个RIT,接下来可以通过 Session Resumption 减少到一个 RTT。
在TLS中最主要的还是两种加密方式–对称加密和非对称加密。由于篇幅有限,这里就简单介绍下。

对称加密

对称加密就是两边都有相同的密钥,两边都知道如何将消息加密解密。
这种加密方式虽然很好,但是由于密钥也是通过网络传输,一旦密钥被截获,那就没有加密解密的必要了。

非对称加密

非对称加密有公钥和私钥之分,公钥所有人都可以知道,通常情况下,服务端同时生成公钥和私钥,然后将公钥发送到客户端,私钥自己保存。客户端收到公钥后,利用公钥将数据加密,然后将密文发送到服务端,密文必须通过私钥才能被解密。这种加密方式就可以完美解决对称加密的弊端。
关于TLS握手的情况比较复杂,由于篇幅有限,暂时不在此细说,感兴趣的童靴可以自行Google。

请求数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用主要是将请求合理的分发到多台服务器。

五:服务端响应HTTP请求并返回处理后的数据

这里假设服务端响应的是一个HTML文件,当浏览器接收到服务端返回的响应后,首先浏览器会判断请求的状态码,如果是200,就继续解析,如果是400或500的话就提示报错,如果是300的话就会重定向,这里也有个重定向的次数问题,浏览器不可能去无限重定向,当重定向次数达到一个上限时依旧会报错。
如果服务端和浏览器之间不需要在进行数据交互,那么此时就需要关闭连接,也就是所谓的TCP四次挥手,关于TCP的四次挥手过程,我在这里做一个简单的描述,先看下面的一张图:
TCP四次挥手
TCP连接的关闭需要发送四次数据包,因此叫做四次挥手,也叫做改进的三次握手,客户端和服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。

  1. 第一次挥手(FIN=1; seq=x):假设客户端想要关闭连接,客户端发送一个FIN标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。数据包发送完毕后,客户端进入FIN_WAIT_1状态。
  2. 第二次挥手(ACK=1; Ack=x+1):服务器端确认客户端的FIN包,发送一个确认包,表明自己接收到了客户端关闭连接的请求,但还没有做好关闭连接的准备。发送完毕后,服务端进入CLOSE_WAIT状态,客户端接收到这个确认包之后,进入FIN_WAIT_2状态,等到服务器端关闭连接。
  3. 第三次挥手(FIN=1, seq=y):服务器端准备好关闭连接时,向客户端发送一个FIN标志位置为1的结束请求包,发送完毕后,服务器端进入LAST_ACK状态,等待来自客户端最后一个ACK。
  4. 第四次挥手(ACK=1, Ack=y+1): 客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入TIME_WAIT状态,等待可能出现要求重传ACK包。服务器端接收到这个确认包之后,关闭连接,进入CLOSE状态。客户端等待了某个固定的时间(两个最大段生命周期)之后,没有收到服务器端的ACK,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入CLOSED状态。

六:浏览器解析服务端返回的数据

当状态码为200的时候,浏览器会继续解析文件,如果文件时gzip格式的,首先会进行解码,然后通过文件的编码格式决定如何去解码文件。

七:浏览器对页面进行布局渲染

这一部分较为复杂,打算另开一篇博客详细讲解…

扩展阅读

  1. 为什么连接的时候是三次握手,而断开时却需要四次挥手?
    答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端收到了发过来的报文。只有等到Server端所有的报文都发送完了,才能发送FIN报文,因此不能一起发送。所以需要四步握手。
  2. 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
    答:虽然按照四次挥手的原理分析,当四个报文都发送完毕了,就可以直接进入到CLOSE状态了,但是我们必须要考虑到网络的不可靠性,有可能最后一个ACK确认报文丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK确认报文。
  3. TCP 建立连接为什么需要三次握手,而不是两次或者四次?
    答: 假设TCP建立连接只有两次握手,那么只要完成了前两次握手服务端就默认完成了连接,就需要为与客户端的连接保留一部分资源。但是我们不能保证网络一定可靠,假如客户端并没有收到服务端的回应,那么客户端默认没有建立连接。如果出现大量这样的情况会导致服务端崩溃。其实三次握手完全就可以确认之前的通信情况,后面继续进行就是徒劳的。

本文结束,谢谢您的阅读。