# 网络模型
TCP/IP的五层模型:
应用层:为应用提供网络服务。
传输层:为上层协议提供端到端的 可靠透明 的数据传输服务,包括处理
差错控制
和流量控制
等问题。网络层:通过IP寻址建立两个节点间的连接,为源端运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传输给目的端的运输层。
数据链路层:网络层规划数据包的传输路线,而数据链路层就是传输路线,且数据链路层还增加了差错控制功能。
物理层:通过物理介质传输比特流。
特点:对等通信,为了使数据分组从源传输到目的地,源端OSI模型(或TCP/IP模型)的每一层都必须与目的端的对等层进行通信。
# TCP与UDP
# TCP
TCP(传输控制协议),是一种面向连接的、可靠的、基于字节流的传输层通信协议。特点:
面向连接
:指发送数据之前必须在两端建立连接(三次握手),这样能建立可靠的连接。仅支持单播传输
:每条TCP传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播。面向字节流
:TCP不像UDP那样一个一个报文独立传输,而是在不保留报文边界的情况下以字节流方式进行传输。可靠传输
:TCP为了保证报文传输的可靠,给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。接收端实体对已成功收到的字节发回一个相应的确认(ACK);如果在合理的往返延迟(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。提供拥塞控制
:当网络出现拥塞时,TCP能够减小向网络注入数据的速率和数量,缓解拥塞。提供全双工通信
:TCP运行通信双方的应用程序在任何时候都能发送数据,因为TCP连接的两端都设有缓存,用来临时存放双向通信的数据。TCP可以立即发送一个数据段,也可以缓存一段时间以便一次发送更多的数据段(最大的数据段大小取决于MSS)。
TCP连接的端点是套接字(IP地址:端口号)。TCP的头部:
# UDP
UDP(用户数据报协议),和TCP协议一样用于处理数据包,是一种无连接的协议。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是报文发送后,无法得知其是否安全完整到达。特点:
面向无连接
:不需要再发送数据前建立连接,想发数据就可以开始发送。- 在发送端,应用层将数据传递给传输层的 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络层了。
- 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会进行任何拼接操作。
有单播、多播、广播功能
面向报文
:UDP对应用程序交下来的报文,既不拆分也不合并,只是保留这些报文的边界,在添加首部后就向下交付IP层。不可靠性
:一方面是因为无连接,且传递时也不备份数据;另一方面是因为UDP没有拥塞控制,一直以恒定速度发送数据,在网络不好的情况下可能导致丢包,但是对某些实时性要求搞的场景(比如电话会议)就需要使用UDP而不是TCP。头部开销小
:UDP头部包含以下几个数据:- 两个十六位的端口号,源端口(可选)和目标端口
- 整个数据报文的长度
- 整个数据报文的检验和(IPv4可选),用于发现头部信息和数据中的错误
UDP的头部只有8字节,相比TCP的至少20字节要少的多,在传输数据报文时很高效。
# 两者区别
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不适用流量控制和拥塞控制 | 可靠传输(数据顺序和正确性),使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多,多对一和多对多相互通信 | 一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
报文长度 | 由应用进程给出 | 根据对方给出的窗口值和当前网络拥塞程度来决定一个报文段包含多少个字节 |
适用场景 | 适用于实时应用,如视频会议、直播、QQ聊天 | 适用于要求可靠传输的应用,如文件传输、邮件、远程登录 |
使用UDP或TCP的应用层协议:
# UDP为什么不可靠?
- 不保证消息交付:不确认,不重传,无超时
- 不保证交付顺序:不设置包序号,不重排,不会发生队首阻塞
- 不跟踪连接状态:不建立连接或重启状态机
- 不进行拥塞控制:不内置客户端或网络反馈机制
# TCP的可靠传输机制
TCP的可靠传输机制是基于连续ARQ协议
和滑动窗口协议
的。
滑动窗口协议:TCP的发送方和接收方分别维持一个发送窗口和接收窗口,控制发送方和接收方所能发送和接收分组的数量和编号。在没有收到接收方确认的情况下,可以连续把窗口内的数据全部发送出去。如果发送窗口中的数据都发送出去但是还没有收到确认,必须停止发送。
连续ARQ协议:接收方一般采用累计确认的方式,即对按序到达的最后一个分组发送确认。每收到一个确认,发送方就把发送窗口向前滑动到确认报文段的后一个位置。如果设置的重传时间到了但还没收到确认,就需要重传这一报文段。
TCP标准没有规定对不按序到达的数据应如何处理。通常是先将失序的报文段缓存起来,重传时只重传失序的报文段(即
选择确认SACK
,在TCP首部添加这个选项,以便报告收到的不连续的字节块的边界)。采用回退N(Go-Back-N)
方法进行重传。
注:发送窗口的大小是变化的,它是由接收窗口剩余大小和网络中拥塞程度来决定的(发送窗口=min(接受方窗口值,拥塞窗口值)),TCP通过控制发送窗口的长度来控制报文段的发送速率。
# TCP的重传机制
由于TCP的下层网络(网络层)可能出现丢失、重复或失序的情况,TCP协议提供可靠数据传输服务。为保证数据传输的正确性,TCP会重传其认为已丢失(包括报文中的比特错误)的包。TCP使用两套独立的机制来完成重传,一是基于时间,二是基于确认信息。
TCP在发送一个数据之后,就开启一个计时器,若是在这个时间内没有收到发送数据的ACK确认报文,则对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号。
其中重传时间的选择是TCP最复杂的问题之一,太短引起报文不必要的重传,太长降低传输效率。TCP采用了一种自适应算法,超时重传时间RTO略大于加权平均往返时间RTTs。如果有报文段重传,就把RTO增大2倍,直到不发生报文段的重传,才重新计算RTTs来更新RTO的数值。
# TCP的流量控制机制
TCP采用大小可变的滑动窗口进行流量控制,窗口大小的单位是字节,这里说的窗口大小其实就是每次传输的数据大小。
- 如果接收缓存已满,就向发送方发送一个零窗口通知,发送方收到后启动一个持续计时器。
- 如果持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。
- 若窗口仍然是零,则重新设置持续计时器;若不是零,则可以继续发送数据。
# TCP的拥塞控制机制
TCP采用基于拥塞窗口cwnd
的方法进行拥塞控制,发送端利用cwnd根据网络的拥塞情况调整发送的数据量。
拥塞的判断:重传定时器超时(已拥堵);收到3个重复的ACK(可能拥堵)。
TCP有4种拥塞控制算法:
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
先来看一张图:
# 慢开始
思路:开始的时候不要发送大量数据,而是先测试一下网络的拥塞程度,在每收到一个新的分组确认就将cwnd加一(初始cnwd为2~4个,旧版为1~2个)。
注:为什么说每收到一个新分组的确认cwnd+1,但是上图是指数级增长的?其横坐标为传输轮次
,就是把拥塞窗口所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认,即一个传输轮次发送的是一个拥塞窗口的量。
一个传输轮次所经历的时间就是往返时间RTT,如拥塞窗口cwnd=4,RTT=连续发送4个报文段+4个报文段的确认的时间。
为了防止cwnd增长过大引起网络拥塞,设置一个慢开始门限(ssthresh):
- 当cnwd < ssthresh,使用慢开始算法
- 当cnwd = ssthresh,既可使用慢开始算法,也可以使用拥塞避免算法
- 当cnwd > ssthresh,使用拥塞避免算法
# 拥塞避免
思路:让拥塞窗口cwnd缓慢,虽然无法完全避免出现拥塞,但是可以使网络比较不容易出现拥塞。即每经历一个传输轮次,cwnd=cwnd+1(加法增大
)。
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(重传定时器超时):
- ssthresh = max(cwnd/2, 2)
- cwnd = 1
- 执行慢开始算法
目的:迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
注:重传定时器超时也可能是确认分组丢失,但是因为无法判定,所以都当作拥塞来处理。
# 快重传
快重传要求接收方再收到一个失序的报文段就立即发出重复确认,为的是发送方及早知道有报文段没有到达对方。发送方只要一连收到3个重复确认,就立即进行重传,而不必等设置的重传计时器时间到期。
由于不需要等待设置的重传计时器到期,能尽早重传未被确认的报文段,提高了整个网络约20%的吞吐量。
# 快恢复
当发送方收到3个重复确认时,由于发送方现在认为网络很可能没有发生堵塞,因此不执行慢开始算法,而是执行快恢复算法:
乘法减小
:ssthresh = cwnd/2- cwnd = ssthresh
- 执行拥塞避免算法。
# 流量控制与拥塞控制
流量控制
是抑制发送端发送数据的速率,使得接收端来得及接收,是端到端的问题。拥塞控制
是防止过多的数据注入到网络中,使网络中的路由器或链路不过载,是一个全局性的过程,涉及到与降低网络传输性能有关的所有因素。
# TCP的三次握手
目的:确认双方的接收能力和发送能力是否正常,连接服务器指定端口,建立TCP连接,并同步双方的序列号和确认号,交换TCP窗口大小信息。
- 第一次握手:Client给Server发送报文,Server知道自己能接收到Client发送的报文
- 该报文的SYN = 1, seq = x
- 第二次握手:Server给Client发送报文,Client知道自己能接收Server发送的报文,且自己发送的报文能被Server接收
- 该报文的SYN = 1, ACK = 1,确认号 = x + 1, seq = y
- 第三次握手:Client给Server发送报文,Server知道自己发送的报文能被Client接收。
- 该报文的ACK = 1,确认号 = y + 1
经过三次握手,客户端(Client)和服务端(Server)都知道自己发送的报文能被对方接收,也知道自己能接收到对方的报文。
注:
- SYN(同步位) / ACK(确认位) / FIN(终止位) 为TCP报文头部的一个标识。
- seq为报文的序列号(Sequence number),ack为报文的确认序号(Acknowledgement Number)。
# 为什么不两次或四次握手?
主要是为了防止已失效的连接请求报文突然又传送到了服务器,从而产生错误。
假设有这样一种场景,A发出的第一个连接请求报文段并未丢失,而是在某些网络结点长时间滞留,以致延误到连接释放以后的某个时间才到达服务器。
- 由于TCP客户端迟迟没有收到确认报文,以为服务器没有收到,因此重新向服务器发送报文建立连接。
- 连接释放后,服务器又收到了第一次发送的已经失效的连接请求报文,误认为是客户端又发出一次新连接请求,于是同意连接。
2次握手在服务器确认后,就建立了新连接,但是客户端现在并没有发连接请求,所以不会理睬。而服务器一直在等客户端发来数据,导致服务器资源白白浪费。
如果采用的是3次握手,那失效报文传给服务器后,服务器回复了确认报文,但是客户端不会再次发送确认,由于服务器收不到确认,就知道客户端并没有请求连接。
至于4次握手,既然3次就可以,那多一次不就是浪费资源嘛。
# TCP的四次挥手
TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK,单独一方的连接释放都只代表单方面不能向对方发送数据。
数据传输结束后,通信双方都可释放连接。假设是客户端先发起关闭请求:
- 第一次挥手
- 若客户端 A 向服务端 B 发送连接释放请求,停止再发送数据,主动关闭TCP连接,等待服务端确认。
- 第二次挥手
- B 收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,表示 A 到 B 的连接已经释放,不接收 A 发的数据了。但是因为 TCP 连接时双向的,所以 B 仍旧可以发送数据给 A。
- 第三次挥手
- B 如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入LAST-ACK状态。
- PS:通过延迟确认的技术,可以将第二次和第三次握手合并,延迟 ACK 包的发送(通常有时间限制,否则对方会误认为需要重传)。
- 第四次挥手
- A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。当 B 收到确认应答后,进入 CLOSED 状态。
- A 的 TIME-WAIT 状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃)时间,防止给 B 的确认报文丢失或出错,导致服务器不能正常关闭。若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。
# 为什么需要四次挥手?
关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送,故需要四次挥手。
# DNS协议
作用:将域名解析为IP地址。客户端向DNS服务器(DNS服务器有自己的IP地址)发送域名查询请求,DNS服务器告知客户机Web服务器的 IP 地址。
# 域名解析过程
当用户在地址栏键入并敲下回车键之后,域名解析就开始了:
- 在
浏览器的缓存
中查找对应IP地址,如果找到直接返回,否则进行下一步 - 如果上一步没有完成对域名的解析过程,浏览器回去
系统缓存(C:\Windows\System32\drivers\etc的hosts文件)
中查找系统是否缓存过这个域名对应的地址,如果没有则向本地DNS服务器发起请求。 - 将请求发给本地DNS服务器,在其缓存中查询,如果找到直接返回,否则进行下一步
- 本地DNS服务器向根域名服务器发送请求,根域名服务器会返回一个所查询域的顶级域名服务器地址
- 本地DNS服务器向顶级域名服务器发送请求,接受请求的服务器查询自己的缓存,如果有记录,就返回查询结果,如果没有就返回相关的下一级的权威域名服务器的地址
- 本地DNS服务器向权威域名服务器发送请求,域名服务器返回对应的结果
- 本地DNS服务器将返回结果保存在缓存中,便于下次使用
- 本地DNS服务器将返回结果返回给浏览器
注:前两步都是在本机上完成的,从第三步开始才正在向本地DNS服务器发起域名解析请求。
# 递归查询和迭代查询
- 递归查询:查询请求发出后,域名服务器代为向下一级域名服务器请求,最后向用户返回查询的最终结果。使用递归查询,用户只需要发出一次查询请求。
- 迭代查询:查询请求后,域名服务器返回单次查询的结果,下一级的查询由用户自己请求。使用迭代查询,用户需要发出多次请求。
实际上,DNS解析是一个包含迭代查询和递归查询的过程。
# DNS同时使用TCP和UDP协议?
DNS占用53号端口,同时使用TCP和UDP协议。
- 区域传输时使用TCP
- 辅域名服务器(DNS服务器)会定时(一般3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步。
- 区域传输使用TCP而不是UDP,因为数据同步传送的数据量比一个请求应答的数据量要多得多。
- 域名解析时使用UDP
- 客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用3次握手,这样DNS服务器负载更低,响应更快。
# DNS记录和报文
DNS服务器以资源记录的形式存储信息,每一个DNS响应报文一般包含多条资源记录,格式为:
(Name, Value, Type, TTL)
TTL是资源记录的生存时间,它定义了资源能被其他的DNS服务器缓存多长时间。
Type有4种常用值,不同值对应的资源记录意义不同:
- 若Type=A,则Name为主机名,Value是主机名对应的IP地址,提供了标准的主机名到IP地址的映射。
- 若Type=NS,则Name是域名,Value是负责该域名的DNS服务器主机名。该记录一般用于DNS链式查询时,返回下一级需要查询的DNS服务器信息。
- 若Type=CName,则Name为别名,Value为该主机的规范主机名。主要是为了给一些复杂的主机名提供一个便于记忆的简单的别名。
- 若Type=MX,则Name为邮件服务器别名,Value为邮件服务器的规范主机名,作用与CName一样。
# HTTP协议
# GET与POST
本质上来说,二者之间没有太大的区别,但两者在设计时就区分了使用方法。
- POST支持多编码(Content-Type);
- GET的长度受限制:HTTP规范中并没有对GET或POST长度作出限制。但特定的浏览器和服务器会对URL长度做出限制,不同浏览器长度限制不同,如IE对URL限制为2083个字节,这是在所有浏览器中最小的。浏览器和服务器都会对POST的请求主体长度作出限制,但这个限制对于一般数据已经足够用了。请求长度的限制由各浏览器和web服务器的处理能力来设定。
- POST相对安全:
- GET请求参数是放在url上(header),而POST的请求参数会放在请求体中(body),但其实POST也不够安全,因为HTTP是明文传输的
- GET请求会被浏览器缓存,POST一般不做缓存:GET请求会被保存在浏览器的浏览历史里面,如果密码等重要数据通过GET提交,别人查看历史记录就会看到这些私密数据。
- GET请求可以直接通过浏览器访问,支持刷新和后退,POST不行:浏览器的加入收藏夹功能就是通过缓存get请求的数据实现的,而访问POST,POST会再次提交。
- POST会分两阶段发送数据:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
# HTTP常见请求头和响应头
请求头:
Accept:浏览器能够处理的内容类型
Accept-Charset:浏览器能够显示的字符集
Accept-Encoding:浏览器能够处理的压缩编码
Accept-Language:浏览器当前设置的语言
Connection:浏览器与服务器之间连接的类型
Cookie:当前页面设置的任何Cookie
Host:发出请求的页面所在的域
Referer:发出请求的页面的URL
User-Agent:浏览器的用户代理字符串
If-None-Match:上次资源返回的ETag值
If-Modified-Since:上一次资源返回的Last-Modified值
2
3
4
5
6
7
8
9
10
11
响应头:
Date:表示消息发送的时间,时间的描述格式由rfc822定义
Server:服务器名称,标示自己用的是什么web服务器
Connection:浏览器与服务器之间连接的类型
Content-Type:表示后面的文档属于什么MIME类型
Set-Cookie:设置cookie
Location:告诉浏览器加载另一个页面,通常在执行 301 重定向时使用
expires:资源过期时间
Cache-Control:控制HTTP缓存
ETag:资源经哈希运算生成的字符串
Last-Modified:资源上次修改的时间
access-control-allow-origin:允许跨域的源地址
access-control-allow-credentials:值为true表示CORS下允许携带cookie
2
3
4
5
6
7
8
9
10
11
12
# Content-Type属性值
application/x-www-form-urlencoded
:浏览器原生form表单,如果不设置enctype属性,最终就会以这种方式提交数据。数据放在body中,按照 key1=val1&key2=val2 的方式进行编码,其中key和val都进行了URL转码。multipart/form-data
:也是一种常见的POST提交方式,通常表单上传文件时使用。application/json
:服务器消息主体是序列化后的JSON字符串。text/xml
:主要用来提交XML格式的数据。
# HTTP状态码
- 1XX 通知
- 100 客户端重新发请求
- 101 更改协议,http,https,http1.0,1.1,2.0
- 2XX 成功
- 200 操作成功
- 201 创建新资源
- 202 请求不被实时处理
- 204 请求成功,但是报文不含实体的主体部分
- 205 请求成功,但是报文不含实体的主体部分,要求客户端重置内容
- 3XX 重定向
- 301 永久性重定向
- 302 临时重定向
- 303 资源存在另一个URL
- 304 允许访问资源,但实体为空,让客户端调用缓存内容
- 4XX 客户端错误
- 400 请求报文语法错误
- 401 请求需要通过验证(认证证书)
- 403 请求资源存在,但是被拒绝
- 404 找不到资源
- 405 不支持的请求方法
- 5XX 服务端错误
- 500 执行请求发生错误
- 501 不支持请求方法
- 502 代理与上行服务器之间出现问题
- 503 服务器暂时超负荷
# 常见的HTTP请求方法
- GET: 向服务器获取数据;
- POST:将实体提交到指定的资源,通常会造成服务器资源的修改;
- PUT:上传文件,更新数据;
- DELETE:删除服务器上的对象;
- HEAD:获取报文首部,与GET相比,不返回报文主体部分;
- OPTIONS:询问支持的请求方法,用来跨域请求;
- CONNECT:要求在与代理服务器通信时建立隧道,使用隧道进行TCP通信;
- TRACE: 回显服务器收到的请求,主要⽤于测试或诊断。
# HTTP 1.0 和 1.1 的区别
- HTTP1.0默认使用非持久连接,造成额外开销;HTTP1.1默认使用持久连接(Connection:keep-alive),并且支持管线化(Pipeline)。目前对于同一个域,大多数浏览器支持同时建立 6 个持久连接。。
- HTTP1.1新增了Host字段,可以实现虚拟主机(在一台物理服务器上可以存在多个虚拟主机,并且它们共享一个IP地址)。
- HTTP1.1比1.0新加了ETag,If-Node-Match,Cache-Control等用于缓存控制的头部。
- HTTP1.1新增24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突。
- HTTP1.1比1.0新增了PUT、HEAD、OPTIONS等请求方法。
- HTTP1.1对带宽进行优化,引入了range头域,只允许请求资源的某个部分;而HTTP1.0不允许断点续传,只能传输整个对象,而不能传输对象的一部分,存在带宽浪费的问题。
注:管线化指可以在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。但是服务器还是按照顺序回应请求。如果前面的回应特别慢,后面就会有许多请求排队等着。这称为队头堵塞。
# HTTP 1.1 和 2.0 的区别
- HTTP2.0采用的彻底的二进制格式传输,取代了HTTP1.x的报文头信息必须是文本格式(ASCII编码),数据可以是文本或二进制的传输。
- 多路复用。在HTTP2.0中有两个概念,分别是
帧(frame)
和流(stream)
,帧表示最小的单位,每个帧都会标识出该帧属于哪个流。多路复用指的是一个TCP连接中可以存在多个流,也就是说,同一时间可以发送多个请求或响应,而且不用按照顺序一一发送,这样就避免了"队头堵塞"的问题。 - 头部压缩。由于 HTTP 1.1 协议不带状态,每次请求都必须附上所有信息,且很多请求字段都是重复的。HTTP 2.0 对报文的头部进行压缩,在客户端和服务端都维护着一份字典,跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送,减少冗余数据,降低开销。
- 服务端推送(Server Push)。服务端可以预测客户端需要的资源,未经请求主动将静态资源推送给客户端,相对减少一些延迟时间。
注:队头阻塞是由 HTTP 基本的“请求 - 应答”模型所导致的。HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。队列里的请求是没有优先级的,只有入队的先后顺序,排在最前面的请求会被最优先处理。如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本,造成了队头堵塞的现象。可以将域名分出很多二级域名,它们都指向同样的一台服务器,能够并发的长连接数变多,从而解决队头阻塞的问题。
# HTTP 3.0
HTTP/3基于UDP协议实现了类似于TCP的多路复用数据流、传输可靠性等功能,这套功能被称为QUIC协议。
- 流量控制、传输可靠性功能:QUIC在UDP的基础上增加了一层来保证数据传输可靠性,它提供了数据包重传、拥塞控制、以及其他一些TCP中的特性。
- 集成TLS加密功能:目前QUIC使用TLS1.3,减少了握手所花费的RTT数。
- 多路复用:同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,解决了TCP的队头阻塞问题。
- 快速握手:由于基于UDP,可以实现使用0 ~ 1个RTT来建立连接。
# HTTPS协议
HTTPS:在HTTP的基础上用TLS/SSL进行身份验证、信息加密和完整性校验。
SSL是TLS的前身,现在绝大部分浏览器都不支持SSL,而是支持TLS。TLS协议采用了对称加密和非对称加密两种方式:
对称加密:发送方和接收方使用相同的规则来为数据进行加密,用同样的钥匙来解开密文,这就是对称加密。如果第三方知道加密规则,就很容易破解。
非对称加密:用两个密钥(即私钥和公钥)来进行加密和解密,数据经过公钥加密只能被私钥解密,数据经过私钥加密只能被公钥解密。服务端拥有成对的私钥和公钥,然后公布自己的公钥让客户端知道,客户端把自己的数据用公钥加密(加密后只能用服务端的私钥解密)。
# 什么是数字证书?
签名
:使用一种 Hash 算法来对公钥和其他信息进行加密,生成一个信息摘要,然后让有公信力的认证中心(简称CA,也就是Certificate Authority证书授权中心)用它的私钥对消息摘要加密,形成签名。数字证书
:将原始的信息和签名合在一起,称为数字证书。
证书中包括:签发者、证书用途、使用者公钥、使用者私钥、使用者的HASH算法、证书到期时间等。
当接收方收到数字证书的时候,先根据原始信息使用同样的 Hash 算法生成一个摘要,然后使用公证处的公钥来对数字证书中的摘要进行解密,最后将解密的摘要和生成的摘要进行对比,就能发现得到的信息是否被更改了。这个方法最要的是认证中心的可靠性,一般浏览器里会内置一些顶层的认证中心的证书,相当于我们自动信任了他们,只有这样才能保证数据的安全。
# HTTPS握手过程
客户端->服务端(Client Hello):支持的TLS版本和支持的加密套件,生成随机数1发给服务端
服务端->客户端(Server Hello):服务端确认支持的TLS版本以及选择的加密套件,并且也生成随机数2发给客户端
服务端->客户端(Certificate):向客户端出示自己的证书,这样浏览器就可以根据对照自己的证书列表来确认这个服务器是否可信
服务端->客户端(Server Key Exchange):把公钥发送给客户端
服务端->客户端(Server Hello done):表示发送完了
客户端->服务端:客户端会生成随机数3(预主密钥),随机数3会用刚刚收到的公钥进行加密,并把加密后的随机数发送给服务器;告诉服务器往后的数据就用商议好的算法和密钥来加密;告诉服务器这边的TLS协商已经没有问题了,加密开始
服务端->客户端(Encrypted Handshake Message):服务端收到加密后的预主密钥后用自己的私钥解密,这样只有客户端和服务端知道预主密钥;告诉客户端服务器这边准备好了,也表示TLS的握手已经成功
然后客户端和服务端分别用第一随机数+第二随机数+预主密钥计算得到会话密钥,这样两者的会话密钥都是相同的,接着可以使用对称加密方式来进行通信。会话密钥只应用在当前会话,提高了安全性。
# HTTPS缺点
- HTTPS需要做服务器和客户端双方的加密和解密处理,耗费更多服务器资源,过程复杂;
- HTTPS协议握手阶段比较费时,增加页面的加载时间;
- SSL证书是收费的,功能越强大的证书费用越高;
- HTTPS连接服务器端资源占用高很多,支持访客稍多的网站需要投入更大的成本;
- SSL证书需要绑定IP,不能再同一个IP上绑定多个域名。
# 当在浏览器中输入URL并回车后发生了什么?
浏览器解析URL获取协议,域名,端口,路径
查看浏览器是否有资源的缓存
- 有。判断是否过期
- 没过期。直接读取缓存
- 过期。
- Etag和If-None-Match
- Last-Modify和lf-Modified-Since
- 文件修改了则把新资源发给浏览器(状态码200),没修改则告诉浏览器读取缓存(状态码304)
- 没有则进行下一步
- 有。判断是否过期
首先进行DNS解析
- 寻找浏览器是否存在缓存,若没有
- 寻找操作系统是否存在缓存,若没有
- 寻找hosts文件中是否有域名和ip的对应关系,若没有
- 查找路由器中是否有缓存
- 寻找DNS服务器是否没缓存,若没有
- 向根域名服务器发送请求
生成HTTP请求
建立TCP连接,三次握手
- 客户端发送一个SYN=1,Seq=X的TCP包
- 服务端发回一个SYN=1,ACK=X+1,Seq=Y的TCP包
- 客户端发送ACK=Y+1,Seq=Y + 1的TCP包
如果是HTTP请求
对HTTP报文进行报文分割并标记序号和端口号
如果是HTTPS请求
- 将HTTP报文交给TLS处理,TLS和服务端进行TLS握手,交换版本信息,加密算法,压缩算法,随机数(浏览器一个,客户端一个)。
- 服务端发送证书,浏览器用CA的公钥对其进行验证。
- 浏览器用服务端的公钥加密生成的预备主密码发送给服务端,两个随机数和预备主密码生成主密码
- 使用主密码生成对称加密的密钥对,消息认证码的密钥对,对称加密的CBC分组(分组模式)需要的初始化向量密钥对。
- 握手之后进行加密,对HTTP报文分组,分组后压缩,压缩后的数据和MAC一起加密。
- 对称加密保障私密性,消息认证码保障完整性,数字证书保障认证,防止中间人攻击。
对TCP报文打包,加入源IP地址和目标IP地址。
根据目标IP地址和路由表,查询下一跳路由。使用ARP查询下一跳路由的MAC地址。
对IP报文打包并附上MAC地址。
发送数据,服务端接收到请求并返回响应。
浏览器接收到HTTP响应,关闭TCP连接或保持复用,四次挥手。
(如果返回了HTML)根据响应头的字符集进行解码
如果响应头没有字符集,则浏览器会默认用一套解码规则,当解析html解析到meta标签中的编码规则时,则替换成新的解码方式重新解码。
资源预解析,会将一些请求资源提前加入请求队列中
解析HTML为DOM树
- 标记化(tokenizing): 将HTML解析成标记
- 构建树(tree construction): 根据标记生成DOM树
解析CSS为CSSOM
根据DOM树和CSSOM生成DOM渲染树
从DOM的根节点遍历所有可见节点,对其应用对应的CSSOM规则。不可见节点包括(script, meta标签, 被css隐藏的节点)
布局:浏览器获取每个渲染对象的位置和尺寸
绘制:将计算好的像素绘制到屏幕
渲染层/合成层合并