第一步 输入网址并解析
浏览器进程/线程模型
- 浏览器进程:浏览器的主进程
- 第三方插件进程
- GPU进程:用于3D绘制
- 浏览器渲染进程(内核):默认每个tab页面一个进程,互不影响,控制页面渲染,脚本执行,事件处理等;
- GUI线程
- js引擎线程
- 事件触发线程
- 定时器线程
- 网络请求线程
从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理 - SegmentFault 思否
解析URL
url一般包括几大部分:
- protocol:协议头,比如http,ftp等
- host:主机域名或ip地址
- port:端口号
- path:目标路径
- query:查询参数
- fragment:即#后的hash的值,一般用来定位到某个地方。
输入URL后,浏览器会解析出协议,主机,端口,路径等信息,并构造一个HTTP请求。
- 浏览器发送请求前,根据请求头expires和cache-control判断是否命中强缓存(包括是否过期)策略,如果命中,直接从缓存获取资源,并不会发送请求,如果没有命中,则进入下一步。
- 没有命中强缓存规则,浏览器会发送请求,根据请求头的if-modified-since和If-None-Match判断是否命中协商缓存,如果命中,直接从缓存获取资源。如果没有命中,则直接从服务端获取资源。
为什么url要解析(即编码)
- 因为网络标准规定了URL只能是字母和数字,但是还有一些其他特殊符号
(-_.~!*'();:@&=+$,/?#[]等,如果不转义会出现歧义。比如url中有一个参数key=value,假如key本身就包含=符号,如ke=y=value,就会出现歧义,就不知道=到底是连接key和value的符号,还是key里面就有=。 - encodeURLComponent编码范围更广,适合给参数编码;encodeURL适合给URL本身编码,项目里一般都是用qs库去处理。
强缓存和协商缓存
- expires和cache-control
| expire | cache-control | |
|---|---|---|
| 版本 | HTTP1.O | HTTP1.1 |
| 来源 | 服务器返回的响应头中 | 响应头和请求头 |
| 语法 | 返回过期日期 | 返回过期的时间 |
| 缺点 | 服务器的时间和浏览器的时间可能不一致导致失效 | 时间最终还是会失效 |
cache-control: 请求头:
| 字段名称 | 说明 |
|---|---|
| no-cache | 告知(代理)服务器不直接使用缓存,要求向原服务器发起请求 |
| no-store | 所有内容都不会被保存到缓存或Internet临时文件中 |
| max-age=delta-seconds | 告知服务器客户端希望接收一个存在时间不大于delta-seconds秒的资源 |
| no-transform | 告知(代理)服务器客户端希望获取实体数据没有被转换过的资源 |
| noly-if-cached | 告知(代理)服务器客户端希望获取缓存的内容(若有),而不用向原服务器发起请求 |
响应头:
| 字段名称 | 说明 |
|---|---|
| public | 表示任何情况下都得缓存该资源 |
| private | 表明返回报文中全部或部分(若指定了field-name则为field-name的字段数据)仅开放给某些用户(服务器指定的share-user,如代理服务器)做缓存使用,其他用户则不能缓存这些数据 |
| no-cache | 不直接使用缓存,要求向服务器发起(新鲜度校验)请求 |
| no-store | 所以内容都不会被保存到缓存或Internet临时文件中 |
| no-transform | 告知客户端缓存文件时不得对实体数据做任何改变 |
| noly-if-cached | 告知(代理)服务器客户端希望获取缓存的内容(若有),而不用向原服务器发去请求 |
-
协商缓存 last-modified(响应头), If-Modefied-Since(请求头); ETag(响应头),If-None-Match(请求头);
-
启发式缓存 如果响应中未显示Expires,Cache-Control: max-age或Cache-Control: s-maxage, 并且响应头中不包含其他有关缓存的限制,缓存可以使用启发式方法计算新鲜度寿命。通常会根据响应头中的2个时间字段Date减去last-Modified值的10%作为缓存时间。
DNS域名解析
在发起http请求之前,浏览器首先要获得我们想访问网页的IP地址,浏览器会发送一个UDP的包给DNS域名解析服务器。
简单来说,域名解析就是将域名转换成IP地址的过程。因为想要访问一台服务器,最终是靠IP地址访问的,而不是靠域名访问的,它们之间的映射关系保存在本地的缓存和网络上的各种域名解析服务器中,如浏览器缓存,系统缓存,域名商的域名解析服务器。
dns解析流程
如果某个用户正在用浏览器mail.baidu.com的网址,当你敲下回车键的一瞬间:
- 1、检查浏览器缓存中是否存在该域名与IP地址的映射关系,如果有则解析结束,没有则继续
- 2、到系统本地查找映射关系,一般在
hosts文件中,如果有则解析结束,否则继续 - 3、到本地域名服务器去查询,有则结束,否则继续
- 4、本地域名服务器查询根域名服务器,该过程并不会返回映射关系,只会告诉你去下级服务器(顶级域名服务器)查询
- 5、本地域名服务器查询顶级域名服务器(即
com服务器),同样不会返回映射关系,只会引导你去二级域名服务器查询 - 6、本地域名服务器查询二级域名服务器(即
baidu.com服务器),引导去三级域名服务器查询 - 7、本地域名服务器查询三级域名服务器(即
mail.baidu.com服务器),此时已经是最后一级了,如果有则返回映射关系,则本地域名服务器加入自身的映射表中,方便下次查询或其他用户查找,同时返回给该用户的计算机,没有找到则网页报错 - 8、如果还有下级服务器,则依此方法进行查询,直至返回映射关系或报错
像该过程中的第1、2、3点,仅限于在本地域名服务器中查找,如果有则直接返回映射关系,否则就去其他DNS服务器中查询,这种查询方式我们叫做递归查询。
第3、4、5、6、7、8过程,他们只会给出下级DNS服务器的地址,并不会直接返回映射关系,这种查询方式叫做迭代查询
html如何做dns优化
前端的DNS优化,可以在html页面头部写入dns缓存地址,比如
<meta http-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
CDN
- 当用户点击网站页面上的内容URL,经过本地DNS系统解析,DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
- CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回用户。
- 用户向CDN的全局负载均衡设备发起内容URL访问请求。
- CDN全局负载均衡设备根据用户IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
- 区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:根据用户IP地址,判断哪一台服务器距用户最近;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后,区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。
- 全局负载均衡设备把服务器的IP地址返回给用户。
- 用户向缓存服务器发起请求,缓存服务器响应用户请求,将用户所需内容传送到用户终端。如果这台缓存服务器上并没有用户想要的内容,而区域均衡设备依然将它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地。
第二步 建立TCP连接
三次握手
一开始,客户端和服务端都处于 CLOSED 状态。客户端主动打开连接,服务端被动打卡连接,结束CLOSED状态,开始监听,进入 LISTEN 状态。
- 一次握手 客户端会随机初始化序号seq = x,将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
- 二次握手 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号seq = y,将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入ack = x + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
- 三次握手 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 ack = y + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。
为什么要三次握手,两次不可以吗?
需要三次握手才能确认双方的接收与发送能力是否正常。
- 第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
- 第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
- 第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。 因此,需要三次握手才能确认双方的接收与发送能力是否正常。
如果是用两次握手,则会出现下面这种情况: 如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一直等待客户端发送数据,浪费资源。
什么是半连接队列
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。 当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据 假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以可以携带数据。
五层因特网协议栈
1. 应用层(DNS,HTTP) DNS解析成ip地址并发送http请求
2. 传输层(tcp,udp)建立tcp连接
3. 网络层(IP, ARP) IP寻址
4. 数据链路层 封装成帧
5. 物理层(利用物理介质传输比特流)物理传输(然后传输的时候通过双绞线,电磁波等各种介质)
osi七层框架:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层
表示层:主要处理两个通信系统中的交换信息的表示方式,包括数据格式贾环,数据加密和解密,数据压缩与终端类型转换等
会话层:具体管理不同用户和进程之间的对话,如控制登录和注销过程。
第三步 发起HTTP请求
get和post的区别
| 分类 | get | post |
|---|---|---|
| 可见性 | 数据在URL中对所有人都是可见的,以 ? 分割url和传输数据,多个参数之间用&连接 | 数据不会显示在URL中,会把请求的数据放在http请求体中 |
| 对数据类型的限制 | 只允许ASCII字符 | 没有限制。也允许二进制数据 |
| 数据长度限制 | url的长度是受限制的(url的最大长度是2048个字符) | 无限制 |
| 安全性 | 与post相比,get的安全性较差,因为所发送的数据是url的一部分。在发送密码或其他敏感信息时绝对不要使用get | post比get更安全,因为参数不会被保存在浏览器历史或web服务器日志中。 |
| 历史 | 参数保留在浏览器历史中 | 参数不会保存在浏览器历史中 |
| 缓存 | 能被缓存 | 不能缓存 |
| 发送 | get会产生一个tcp数据包,head和data一起发送 | post会产生两个tcp数据包,head先发送,服务器响应返回100状态码后继续发送data, |
| 后退按钮/刷新 | 无害 | 数据会被重新提交 |
HTTP发展史
影响一个HTTP网络请求的因素主要有两个:带宽和延时
-
带宽
如果说我们还停留在拨号上网的阶段,带宽可能会成为一个比较严重影响请求的问题,但是现在网络基础建设已经使得带宽得到极大的提升,我们不再会担心由带宽而影响网速,那么就只剩下延迟了。
-
延迟
- 浏览器阻塞(HOL blocking):浏览器会因为一些原因阻塞请求。浏览器对于同一个域名,同时只能有 4 个连接(这个根据浏览器内核不同可能会有所差异),超过浏览器最大连接数限制,后续请求就会被阻塞。
- DNS 查询(DNS Lookup):浏览器需要知道目标服务器的 IP 才能建立连接。将域名解析为 IP 的这个系统就是 DNS。这个通常可以利用DNS缓存结果来达到减少这个时间的目的。
- 建立连接(Initial connection):HTTP 是基于 TCP 协议的,浏览器最快也要在第三次握手时才能捎带 HTTP 请求报文,达到真正的建立连接,但是这些连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。
HTTP1.0和HTTP1.1的区别
- 缓存处理:在http1.0中主要使用If-Modified-Since, Expires来作为缓存的判断标准,HTTP1.1中引入了更多的缓存策略例如If-None-Match,Cache-control等。
- 带宽优化及网络连接的使用:在http1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
- 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。 长连接,HTTP 1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection:keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
HTTP2.0和HTTP1.1相比的新特性
- 新的二进制格式:HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便。
- 多路复用:即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的id将request再归属到各自不同的服务端请求里面。
- header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
- 服务端推送:例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。
HTTP2.0的多路复用和HTTP1.1中的长连接有什么区别
- HTTP1.0:一次请求-响应建立一个连接,用完关闭;每一个请求都要建立一个连接;
- HTTP1.1:若干个请求排队串行化单线程处理,后面的请求等待前面的返回才能获得执行机会,一旦有某个请求超时等,后续请求只能被阻塞。
- HTTP2.0:多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其他连接的正常执行;
HTTP2的缺点
- TCP的队头阻塞:http2解决了应用层面的线头阻塞问题,但是任然是基于TCP协议的,而tcp最初是为了单连接而设计的,在http2中,多个请求如果其中任意一路数据流中出现丢包情况,就会阻塞该tcp连接中的所有请求,不同于http1.1,如果浏览器为每个域名开启了6个tcp连接,如果其中一个tcp连接发生了线头阻塞,那么其他5个连接依然可以继续传输数据
- tcp的建立连接延时:
- tcp协议僵化。
QUIC/HTTP3
QUIC 实现的目标,就是利用 UDP 实现一个 TCP,支持 TCP 的所有特性,并且比 TCP 更快更好用。
QUIC的优点:
-
由于 TCP、UDP 协议是系统内核实现的,更新修改起来并不很方便,而 QUIC 是软件层面实现的,更新迭代起来非常方便。
-
UDP 本身是无序传输的,这在单个连接上并行传输多个数据有天生的优势:多个数据直接发送即可,由 QUIC 对收到的数据进行重新组合排序,然后送往上层应用。这中间不用等待各种数据确认包,效率非常高。
-
在建立 TCP 连接时,需要进行至少三次握手,如果要开启 TLS 加密,则还需要进行 TLS 握手。而 QUIC 采用了类似于 TCP Fast Open 的技术,如果之前连接过,那么之后可以不用重复握手而直接开始传送数据,以实现 0-RTT 往返时延。即便之前没有连接过,也可以在 1-RTT 内完成连接并开始传送数据。并且自身就拥有与 TLS 等效的加密措施。
-
在发生丢包时,TCP 会重传丢失的包。而 QUIC,则使用了一种非常神奇的前向纠错算法,通过连续的几个数据包的校验和,可以直接恢复出丢失的包内容,而不需要重传。
-
在移动端表现更好:用户的网络环境并不稳定,Wi-Fi、4G、3G、2G 之间来回变化,IP 一旦发生变化,TCP 的连接是不可能保持的。而 QUIC 就不存在这样的问题,通过 ID 来标识用户(而不是 IP + 端口),在连接切换后直接恢复之前的连接会话。
-
配合 HTTP/2 API 食用更佳:由于 HTTP/2 采用二进制帧传输机制,QUIC 直接使用这样的机制进行数据传输,效率更高!
HTTPS
HTTP + 加密 + 认证 + 完整性保护 = HTTPS;
HTTP协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议TLS/SSL具有身份验证、信息加密和完整性校验的功能,可以避免此类问题发生。
- TLS/SSL工作原理 HTTPS协议的主要功能基本依赖于TLS/SSL协议,TLS/SSL的功能实现主要依赖于三类基本算法:散列函数Hash, 对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性。
- 散列函数Hash 常见的有 MD5、SHA1、SHA256,该类函数特点是函数单向不可逆、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改并验证数据的完整性; 在信息传输过程中,散列函数不能单独实现信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密;
- 对称加密 常见的有AES-CBC、DES、3DES、AES-GCM等,相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是1对1; 对称加密的优势是信息传输1对1,需要共享相同的密码,密码的安全是保证信息安全的基础,服务器和 N 个客户端通信,需要维持 N 个密码记录,且缺少修改密码的机制;
- 非对称加密 即常见的 RSA 算法,还包括 ECC、DH 等算法,算法特点是,密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,服务器可以实现1对多的通信,客户端也可以用来验证掌握私钥的服务器身份。 非对称加密的特点是信息传输1对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。
结合三类算法的特点,TLS的基本工作方式是,客户端使用非对称加密与服务器进行通信,实现身份验证并协商对称加密使用的密钥, 然后对称加密算法采用协商密钥对信息以及信息摘要进行加密通信,不同的节点之间采用的对称密钥不同,从而可以保证信息只能通信双方获取。
- ssl的大致流程
- 客户端向服务器发送客户端支持的ssl版本 + 支持的加密方式 + 一个随机数
- 服务端接收消息并返回 确认ssl版本和加密方式 + 数字证书 + 一个随机数
- 客户端确认数字证书的可靠性,并发送一个用服务端公钥加密的随机数,同时用前三个随机数通过约定的算法生成对话密钥。
- 服务器用自己的私钥解密这个随机数,并用前三个随机数通过约定的算法生成对话密钥。
- 数字证书
- 签发证书的机构
- 加密算法
- hash算法
- 公钥
- 证书到期的时间
数字证书的作用(身份识别,防止中间人攻击)
在信息传输的过程中,我们通常需要验证信息的发送方的身份,把发送端的公钥发送给接收端,发送端通过把自己的内容使用私钥加密然后发送给接收端,接收端只能用发送端的公钥解密,自然就验证了发送端的身份。
数字证书
在传输的过程中,客户端如何获得服务器端的公钥呢?当时是服务器分发给客户端,如果一开始服务端发送的公钥到客户端的过程中有可能被第三方劫持,然后第三方自己伪造一对密钥,将公钥发送给客户端,当服务器发送数据给客户端的时候,中间人将信息进行劫持,用一开始劫持的公钥进行解密后,然后使用自己的私钥将数据加密发送给客户端,而客户端收到后使用公钥解密,反过来亦是如此,整个过程中间人是透明的,但信息泄露却不得而知。 为了防止这种情况,数字证书就出现了,它其实就是基于上上面所说的私钥加密数据,公钥解密来验证其身份。
数字证书是由权威的CA(Certificate Authority)机构给服务端进行颁发,CA机构通过服务端提供的相关信息生成证书,证书内容包含了持有人的相关信息,服务器的公钥,签署者签名信息(数字签名)等,最重要的是公钥在数字证书中。
数字证书是如何保证公钥来自请求的服务器呢?
数字证书上由持有人的相关信息,通过这点可以确定其不是一个中间人;
但是证书也是可以伪造的,如何保证证书为真呢?
一个证书中含有三个部分:"证书内容,散列算法,加密密文",证书内容会被散列算法hash计算出hash值,然后使用CA机构提供的私钥进行RSA加密。当客户端发起请求时,服务器将该数字证书发送给客户端,客户端通过CA机构提供的公钥对加密密文进行解密获得散列值(数字签名),同时将证书内容使用相同的散列算法进行Hash得到另一个散列值,比对两个散列值,如果两者相等则说明证书没问题。
http报文结构
报文一般包括了:通用头部,请求/响应头部,请求/响应体
- 通用头部
Request URL: 请求的web服务器地址
Request Method:请求方法
Status Code:请求的返回状态码
Remote Address:请求的远程服务器地址(ip)
- 请求/响应头部 常用的请求头部
Accept:接收类型,表示浏览器支持的MIME类型(对应服务端返回的Content-Type);
Accept-Encoding:浏览器支持的压缩类型,如gzip等,超出类型不能接收;
Content-Type:客户端发送出去实体内容的类型。
Cache-Control:指定请求和响应遵循的缓存机制,如no-Cache
If-Modified-Since:对应服务端的Last-Modified,用来匹配看文件是否变动,http1.0
Expires:缓存控制,在这个时间内不会请求,直接使用缓存,http1.0,而且是服务端时间
Max-age:代表资源在本地缓存多少秒,有效时间内不会请求,而是使用缓存,http1.1中
If-None-Match:对应服务端的ETag,用来匹配文件内容是否改变,http1.1
Cookie: 有cookie并且同域访问时会自动带上
Connection: 当浏览器与服务器通信时对于长连接如何进行处理,如keep-alive
Host:请求的服务器URL
Origin:最初的请求是从哪里发起的(只会精确到端口),Origin比Referer更尊重隐私
Referer:该页面的来源URL(适用于所有类型的请求,会精确到详细页面地址,csrf拦截常用到这个字段)
User-Agent:用户客户端的一些必要信息,如UA头部等
常用的响应头部
Access-Control-Allow-Headers: 服务器端允许的请求Headers
Access-Control-Allow-Methods: 服务器端允许的请求方法
Access-Control-Allow-Origin: 服务器端允许的请求Origin头部(譬如为*)
Content-Type:服务端返回的实体内容的类型
Date:数据从服务器发送的时间
Cache-Control:告诉浏览器或其他客户,什么环境可以安全的缓存文档
Last-Modified:请求资源的最后修改时间
Expires:应该在什么时候认为文档已经过期,从而不再缓存它
Max-age:客户端的本地资源应该缓存多少秒,开启了Cache-Control后有效
ETag:请求变量的实体标签的当前值
Set-Cookie:设置和页面关联的cookie,服务器通过这个头部把cookie传给客户端
Keep-Alive:如果客户端有keep-alive,服务端也会有响应(如timeout=38)
Server:服务器的一些相关信息
一般来说,请求头部和响应头部是匹配分析的。譬如,请求头部的Accept要和响应头部的Content-Type匹配,否则会报错;譬如,跨域请求时,请求头部的Origin要匹配响应头部的Access-Control-Allow-Origin,否则会报跨域错误; 譬如,在使用缓存时,请求头部的If-Modified-Since、If-None-Match分别和响应头部的Last-Modified、ETag对应
- 请求/响应实体
http请求时,除了头部,还有消息实体,一般来说
请求实体中会将一些需要的参数都放入进入(用于post请求)。
譬如实体中可以放参数的序列化形式(a=1&b=2这种),或者直接放表单对象(Form Data对象,上传时可以夹杂参数以及文件),等等
而一般响应实体中,就是放服务端需要传给客户端的内容
一般现在的接口请求时,实体中就是对于的信息的json格式,而像页面请求这种,里面就是直接放了一个html字符串,然后浏览器自己解析并渲染。
第四步 返回HTTP响应
HTTP状态码
状态码由3位数字组成,数字中的第一位指定了响应类别。
| 状态码 | 解释 | ||
|---|---|---|---|
| 1xx(信息状态码) | 接收请求正在处理 | ||
| 2xx(成功状态码) | 请求正常处理完毕 | ||
| 200 | 请求成功。一般用于get和post请求 | ||
| 204 | 无内容。服务器成功处理,但未返回内容。在未更新网页的请况下,可以确保浏览器继续显示当前文档 | ||
| 206 | 对资源某一部分的请求,服务器成功处理了部分get请求,响应报文中包含Content-Range指定范围的实体内容。 | ||
| 3xx(重定向状态码) | 需要附加操作以完成请求 | ||
| 301 | 永久性重定向。请求的资源已被永久的移动到新url,返回的信息会包含新的url,浏览器会自动定向到新url。今后任何新的请求都应使用新的url代替。 | ||
| 302 | 临时性重定向。与301类似。但资源只是临时被移动,客户端应继续使用原有url | ||
| 303 | 查看其它地址。与302类似。使用get请求查看 | ||
| 304 | 所请求的资源未修改 | ||
| 307 | 临时重定向。与302类似。使用get请求重定向,会按照浏览器标准,不会从post变成get | ||
| 4xx(客户端错误状态码) | 服务端无法处理请求 | ||
| 400 | 客户端请求报文中存在语法错误,服务器无法理解 | ||
| 401 | 请求要求用户的身份认证 | ||
| 402 | 保留,将来使用 | ||
| 403 | 服务器理解客户端的请求,但是拒绝执行此请求 | ||
| 404 | 服务器无法根据客户端的请求找到资源(网页) | ||
| 5xx(服务器错误状态码) | 服务器处理请求出错 | ||
| 500 | 服务器内部错误,无法完成请求 | ||
| 501 | 服务器不支持请求的功能,无法完成请求 | ||
| 503 | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
不常用:
| 状态码 | 中文描述 |
|---|---|
| 201 | 已创建。成功请求并创建了新的资源 |
| 202 | 已接受。已经接受请求,但未处理完成 |
| 203 | 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 |
| 205 | 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
| 300 | 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
| 305 | 使用代理。所请求的资源必须通过代理访问 |
| 306 | 已经被废弃的HTTP状态码 |
| 402 | 保留,将来使用 |
| 405 | 客户端请求中的方法被禁止 |
| 406 | 服务器无法根据客户端请求的内容特性完成请求 |
| 407 | 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
| 408 | 服务器等待客户端发送的请求时间过长,超时 |
| 409 | 服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突 |
| 410 | 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
| 411 | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
| 412 | 客户端请求信息的先决条件错误 |
| 413 | 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 |
| 414 | 请求的URI过长(URI通常为网址),服务器无法处理 |
| 415 | 服务器无法处理请求附带的媒体格式 |
| 416 | 客户端请求的范围无效 |
| 417 | 服务器无法满足Expect的请求头信息 |
| 501 | 服务器不支持请求的功能,无法完成请求 |
| 502 | 充当网关或代理的服务器,从远端服务器接收到了一个无效的请求 |
| 504 | 充当网关或代理的服务器,未及时从远端服务器获取请求 |
| 505 | 服务器不支持请求的HTTP协议的版本,无法完成处理 |
第五步 浏览器渲染页面
渲染过程
- 构建DOM树:从上到下解析HTML文档生成DOM节点树;
- 构建CSSOM树:加载解析样式生成CSSOM树;
- 执行JavaScript:加载并执行JavaScript代码;
- 构建渲染树:根据DOM树和CSSOM树,生成渲染树;
- 渲染树:按顺序展示在屏幕上的一系列矩形,这些矩形带有字体,颜色和尺寸等视觉属性。
- 布局:根据渲染树将节点树的每一个节点布局在屏幕上的正确位置;
- 绘制:遍历渲染树绘制所有节点,为每一个节点适用对应的样式,这一过程是通过UI后端模块完成。
第六步 断开连接
客户端/服务端断开连接的时机
对于http1.0协议来说
- 如果响应头中有content-length头,则客户端在接收body时,可以依照这个长度来接受数据,接受完后,就表示这个请求完成了;->服务端和客户端在明确自己数据处理完成后,都可以主动断开连接
- 如果没有content-length头,则客户端会一直接收数据,直到服务端主动断开连接,才表示body接收完了。–>客户端必须等待服务端断开连接后,才能断开自己的连接。
对于http1.1协议来说:
- 如果响应头中的Transfer-encoding为chunked传输,则表示body是流式输出,body会被分成多个块,每块的开始会标识出当前块的长度,此时,body不需要通过长度来指定。–>服务端和客户端在明确自己数据处理完成后,都可以主动断开连接
- 如果响应头中的Transfer-encoding为非chunked传输,但有content-length,则按照content-length来接收数据。–>服务端和客户端在明确自己数据处理完成后,都可以主动断开连接
- 如果响应头中的Transfer-encoding为非chunked传输,但没有content-length,则客户端接收数据,直到服务端主动断开连接。–>客户端必须等待服务端断开连接后,才能断开自己的连接。
四次挥手
TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务器均可主动发起挥手动作。
- 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
- 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。 即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
- 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
- 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
挥手为什么需要4次?
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
四次挥手释放连接时,等待2MSL的意义
-
保证客户端发送的最后一个ACK报文段能够到达服务端。
这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到服务端重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态。
-
防止“已失效的连接请求报文段”出现在本连接中。
客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。