在浏览器的地址栏输入一个 URL 后回车,都经过了那些步骤后才会显示一个界面给我们?
在浏览中输入 URL 并且获取响应的过程,其实就是浏览器和该 URL 对应的服务器的网络通信过程。比如我们输入 www.baidu.com,那么会返回一个百度搜索的界面,这其实就是浏览器和百度服务器之间的网络通信过程。浏览器就是客户端,用于发出请求,而百度的服务器就是服务端,用于接收并响应请求。
它主要经过以下几个步骤:
1. DNS域名解析
DNS协议是用来将域名转换为IP地址(也可以将IP地址转换为相应的域名地址)
解析过程大致分为以下六步骤:
- 在浏览器中输入www.baidu.com域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。
- 如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存(浏览器在访问一个网站后,会将对应的DNS解析记录保存在本地),是否有这个网址映射关系,如果有,直接返回,完成域名解析。
- 如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/IP参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,本地DNS服务器查询对应的域名映射,如果包含在本地配置区域资源中,则返回解析结果给客户机。
- 如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。
- 如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(baidu.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找baidu.com域服务器,重复上面的动作,进行查询,直至找到www.baidu.com主机。
- 如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。最后把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机,从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询。
如果是一个不存在的域名,那么浏览器的工作流程会是怎么样的呢?
不存在的域名也会去解析,先执行域名解析的步骤:先访问本地缓存(浏览器缓存,然后是操作系统缓存、host文件)找不到则再逐级DNS(野生DNS服务器-根-顶级-权威),都执行完还是找不到就报错。
域名解析路径
浏览器缓存->操作系统dnscache ->hosts文件->非权威域名服务器->根域名服务器->顶级域名服务器->二级域名服务器->权威域名服务器。
其中非权威域名服务器还包括LDNS(企业内网DNS服务器),三大营运商DNS,谷歌公开的DNS,微软公开的DNS等。
2. 建立TCP连接
TCP/IP 协议实际上是一系列网络通信协议的统称,其中最核心的两个协议是 TCP 和 IP,其他的还有 UDP、ICMP、ARP 等等,共同构成了一个复杂但有层次的协议栈。这个协议栈有四层,最上层是“应用层”,最下层是“链接层”,TCP 和 IP 则在中间:TCP 属于“传输层”,IP 属于“网际层”
IP协议是为了解决互联网访问中的寻址路由问题,IP指的是服务器在互联网上面的地址。
TCP 协议是“Transmission Control Protocol”的缩写,意思是“传输控制协议”,它位于 IP 协议之上,基于 IP 协议提供可靠的、字节流形式的通信,是 HTTP 协议得以实现的基础。
可靠”是指保证数据不丢失,“字节流”是指保证数据完整。
三次握手整个过程分为五步:
- 客户端和服务器端都处于 CLOSE 状态,此时客户端主动打开连接,服务端被动打开连接,服务端状态变为 listen
- 客户端会随机初始化一个 序号(client_isn),将这个序号置于TCP首部的 序号 字段,同时将 SYN标志 置为1,表明是一个SYN报文,然后将这个SYN报文发送给服务端,此时客户端状态变为 syn_sent
- 服务端接收到客户端发送的SYN报文后,此为第一次握手,服务端会随机初始化自己的 序号(server_isn),然后将其放置到TCP首部的 序号 字段,同时将 client_isn + 1 放置到TCP首部的 确认应答号 字段,然后将SYN和ACK字段都置为1,最后将此报文发送给客户端,此时服务端的状态变为 syn_rcvd
- 客户端接收到服务端的报文后,此为第二次握手,客户端此时还要向服务端最后发送一个确认应答报文,首先将TCP首部的ACK字段置为1,然后将确认应答号字段置为 server_isn+1,最后再发送给服务端,此时客户端状态为 established
- 服务端接收到客户端发送的应答报文后,此为第三次握手,状态也变为 established,至此客户端与服务端就可以正式开始通信了
通过对上述步骤的分析,我们可以知道第一次握手时可以让服务端知道客户端的 发送能力 是正常的,第二次握手可以让客户端知道服务端的 发送和接收能力 是正常的,第三次握手可以让服务端知道客户端的 接收能力 是正常的,至此就可以让通信双方确认了对方的发送和接收能力都正常,然后就可以进行可靠的通信了
3. 发起HTTP请求
HTTP/1.1 里唯一要求必须提供的头字段是 Host,它必须出现在请求头里,标记虚拟主机名。
目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式。
GET:获取资源,可以理解为读取或者下载数据;
HEAD:获取资源的元信息;
POST:向资源提交数据,相当于写入或上传数据;
PUT:类似 POST;
DELETE:删除资源;
CONNECT:建立特殊的连接隧道;
OPTIONS:列出可对资源实行的方法;
TRACE:追踪请求 - 响应的传输路径。
在现在前端最常用的 cors 跨域中,浏览器都是用 OPTIONS 方法发预检请求的.
TCP 三次握手完成后,浏览器与目标服务器之间就建立了一个可靠的虚拟通道,于是浏览器就可以发送自己的 HTTP 请求了。
需要注意的是,HTTP 请求报文或者响应报文在 TCP 连接通道上进行传输的时候,由于这些报文比较大,为了更容易和准确可靠的传输,TCP 会将 HTTP 报文按序号分割成若干报文段并加上 TCP 首部,分别进行传输。接收方在收到这些报文段后,按照序号以原来的顺序重组 HTTP 报文。
TTP 协议也是与 TCP/UDP 类似,同样也需要在实际传输的数据前附加一些头数据,不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。
HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
起始行(start line):描述请求或响应的基本信息;
头部字段集合(header):使用 key-value 形式更详细地说明报文;
消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。
这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。
HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”。
4. 接受响应结果
HTTP响应也由三个部分组成,分别是:状态行、相应头部、响应正文。
1.状态行
HTTP-Version Status-Code Reason-Phrase CRLF
其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。
1xx:指示信息–表示请求已接收,继续处理。
2xx:成功–表示请求已被成功接收、理解、接受。
3xx:重定向–要完成请求必须进行更进一步的操作。
4xx:客户端错误–请求有语法错误或请求无法实现。
5xx:服务器端错误–服务器未能实现合法的请求。
常见状态代码、状态描述的说明如下。
200 OK:客户端请求成功。
301: 永久重定向, Location响应首部的值仍为当前URL,因此为隐藏重定向。
302: 临时重定向,显式重定向, Location响应首部的值为新的URL。
304:Not Modified 未修改,比如本地缓存的资源文件和服务器上比较时,发现并没有修改,服务器返回一个304状态码,告诉浏览器,你不用请求该资源,直接使用本地的资源即可。
400 Bad Request:客户端请求有语法错误,不能被服务器所理解。
401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。
403 Forbidden:服务器收到请求,但是拒绝提供服务。
404 Not Found:请求资源不存在,举个例子:输入了错误的URL。
500 Internal Server Error:服务器发生不可预期的错误。
502: Bad Gateway 前面代理服务器联系不到后端的服务器时出现。
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常,举个例子:HTTP/1.1 200 OK(CRLF)。
504:Gateway Timeout 这个是代理能联系到后端的服务器,但是后端的服务器在规定的时间内没有给代理服务器响应
Connection 使用keep-alive特性
Content-Encoding 使用gzip方式对资源压缩
Content-type MIME类型为html类型,字符集是 UTF-8
Date 响应的日期
Server 使用的WEB服务器
Transfer-Encoding:chunked 分块传输编码是http中的一种数据传输机制,允许HTTP由网页服务器发送给客户端应用(通常是网页浏览器)的数据可以分成多个部分,分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供。
5. 浏览器解析html
浏览器按顺序解析html文件,构建DOM树,在解析到外部的css和js文件时,向服务器发起请求下载资源,若是下载css文件,则解析器会在下载的同时继续解析后面的html来构建DOM树,则在下载js文件和执行它时,解析器会停止对html的解析。这便出现了js阻塞问题。
预加载器:
当浏览器被脚本文件阻塞时,预加载器(一个轻量级的解析器)会继续解析后面的html,寻找需要下载的资源。如果发现有需要下载的资源,预加载器在开始接收这些资源。预加载器只能检索HTML标签中的URL,无法检测到使用脚本添加的URL,这些资源要等脚本代码执行时才会获取。
注: 预解析并不改变Dom树,它将这个工作留给主解析过程
浏览器解析css,形成CSSOM树,当DOM树构建完成后,浏览器引擎通过DOM树和CSSOM树构造出渲染树。渲染树中包含可视节点的样式信息(不可见节点将不会被添加到渲染树中,如:head元素和display值为none的元素)
值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
6. 浏览器布局渲染
布局:通过计算得到每个渲染对象在可视区域中的具体位置信息(大小和位置),这是一个递归的过程。
绘制:将计算好的每个像素点信息绘制在屏幕上
7. 总结
在HTTP传输数据的过程中,其中会经过很多的“子协议”,他们各司其职,共同完成了数据传输:
TCP——负责数据传输
IP——负责标识传输对象
DNS——负责简化人类的记忆
URI/L——负责标识传输的资源
SSL——负责数据传输的安全
Proxy——负责信息的中转
像极了走标,
需要搞清楚从哪到哪——IP
需要搞定怎么传输——TCP
需要保障货物的安全——SSL
需要送货的具体位置——URI
需要把目的地的经纬度换成地址名——DNS
需要中间中转一下——Proxy
HTTP——我不管那么多,我向你要什么你就给什么
其实整个的传输过程可以简化为一次送快递的过程:
假设你想把一件毛绒玩具送给朋友,但你要先拿个塑料袋套一下,这件玩具就相当于 HTTP 协议里要传输的内容,比如 HTML,然后 HTTP 协议为它加一个 HTTP 专用附加数据。
你把玩具交给快递小哥,为了保护货物,他又加了层包装再贴了个标签,相当于在 TCP 层给数据再次打包,加上了 TCP 头。
接着快递小哥下楼,把包裹放进了三轮车里,运到集散点,然后再装进更大的卡车里,相当于在 IP 层、MAC 层对 TCP 数据包加上了 IP 头、MAC 头。
之后经过漫长的运输,包裹到达目的地,要卸货再放进另一位快递员的三轮车,就是在 IP 层、MAC 层传输后拆包。
快递员到了你朋友的家门口,撕掉标签,去除了 TCP 层的头,你朋友再拆掉塑料袋包装,也就是 HTTP 头,最后就拿到了玩具,也就是真正的 HTML 页面。