1.1、如何通过三次握手建立连接
首先我们要清楚一个概念:http
请求与TCP
链接之间的关系,在客户端向服务端请求和返回的过程中,是需要去创建一个TCP connection, 因为http是不存在链接这样的概念的,它只有请求和响应的概念,请求和响应都是一个数据包,中间要通过一个传输通道,这个传输通道就是在TCP里面创建了一个从客户端发起和服务端接收的一个链接,TCP 链接在创建的时候是有一个三次握手(三次网络传输)这样一个消耗在的。
imageTCP的核心思想:为了保证传输的可靠。 其次,保证传输效率。
为什么是三次握手: 第一次握手CLIENT告诉SERVER“我将要开始传输数据了”。 第二次握手SERVER告诉CLIENT“我已经知道你将要传输数据了,我已经做好准备”。 第三次握手CLIENT告诉SERVER“我已经知道你已经知道'我知道你已经做好准备'”,SERVER端收到这个信号,开始传输数据。
TCP标示: SYN(synchronous
同步
建立联机)、 ACK(acknowledgement 确认)、 Sequence number(顺序号码)
第一次握手: 建立连接,客户端A发送SYN = 1,随机产生Seq=client_isn的数据包到服务器B,等待服务器确认。
第二次握手:服务器B收到请求后确认联机(可以接收数据), 发起第二次握手请求, ACK=(A的Seq+1)、SYN=1, 随机产生Seq=client_isn的数据包到A。
第三次握手:A收到后检查ACK是否正确。若正确,A会在发送确认包ACK=服务器B的Seq+1、ACK=1, 服务器B收到后确认Seq 值与 ACK值,若正确,则建立连接。
TCP的状态 (SYN, FIN, ACK, PSH, RST, URG) eg
状态 标志状态 备注 SYN 表示建立连接 只是单个的一个SYN,它表示的只是建立连接。 FIN 表示关闭连接 SYN与FIN是不会同时为1的。 ACK 表示响应 TCP的几次握手就是通过这样的ACK表现出来的。 PSH 表示有 DATA数据传输 PSH为1表示的是有真正的TCP数据包内容被传递。 RST 表示连接重置 RST一般是在FIN之后才会出现为1的情况。 URG urgent紧急
1.2、三次握手数据包详细分析
Wireshark(前称Ethereal)是一个网络数据包分析软件。网络数据包分析软件的功能是截取网络数据包,并尽可能显示出最为详细的网络数据包数据。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。使用技巧
ip.addr == 服务器端ip 演示例子
- 第一次握手请求,如下图: 客户端发送一个TCP,标志位为 SYN,Seq(序列号)=0,代表客户端请求建立链接 。
- 第二次握手请求,服务器发回数据包,标志位为[SYN, ACK],ACK设置为客户端第一次握手请求的Seq+1,即ACK=0+1=1,在随机产生一个Seq的数据包到客户端。
- 第三次握手请求,客户端在次发送确认数据包,标识位为ACK,把服务器发来的Seq+1,即ACK=0+1,发送给服务器,服务器成功收到ACK报文段之后,连接就建立成功了。
2.1 浏览器缓存
浏览器缓存控制机制有两种:
HTML Meta标签 (非HTTP协议定义的缓存机制)
HTTP头信息 (HTTP协议定义的缓存机制), 今天主要讲解HTTP头信息控制缓存。
2.1.1 为什么需要浏览器缓存?
我们知道通过HTTP协议,在客户端和浏览器建立连接时需要消耗时间,而大的响应需要在客户端和服务器之间进行多次往返通信才能获得完整的响应,这拖延了浏览器可以使用和处理内容的时间。这就增加了访问服务器的数据和资源的成本,因此利用浏览器的缓存机制重用以前获取的数据就变成了性能优化时需要考虑的事情。
首先,思考两个问题:
-
在页面中引入静态资源文件(css,img,js等),为什么静态资源文件改变后,再次发起请求还是之前的内容,没有变化呢?
-
在使用webpack等一些打包工具时,为什么要加上一串hash码?
2.1.2 浏览器请求流程图
- 浏览器第一次请求流程图(图片来源网络):
- 浏览器再次请求时(图片来源网络):
2.1.3 重要概念 详情
- Etag/If-None-Match:Etag/If-None-Match也要配合Cache-Control使用。
Etag
:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。通常情况下,ETag更类似于资源指纹(fingerprints),如果资源发生变化了就会生成一个新的指纹,这样可以快速的比较资源的变化。
If-None-Match
:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
- Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。
Last-Modified
:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。If-Modified-Since
:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
-
Expires策略
:Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。 -
Cache-control策略
(重点关注):Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age
Cache-Control值 | 说明 | 特性 |
---|---|---|
Public | 指示响应可被任何缓存区缓存。 | 可缓存性 |
Private | 指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。 这允许服务器仅仅描述当用户的部分响应消息, 此响应消息对于其他用户的请求无效。 | 可缓存性 |
no-cache | 指示请求或响应消息不能缓存,该选项并不是说可以设置 ”不缓存“,切勿望文生义~~ | 可缓存性 |
max-age | 指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。 | 到期 |
s-maxage | 会代替max-age,只有在代理服务器(nginx代理服务器)才会生效 | 到期 |
max-stale | 是发起请求方主动带起的一个头,是代表即便缓存过期, 但是在max-stale这个时间内还可以使用过期的缓存, 而不需要愿服务器请求新的内容 | 到期 |
must-revalidate | 如果max-age设置的内容过期,必须要向服务器请求重新获取 数据验证内容是否过期 | 验证 |
proxy-revalidate | 主要用在缓存服务器,指定缓存服务器在过期后重新从原服务器获取, 不能从本地获取 | 验证 |
min-fresh | 指示客户机可以接收响应时间小于当前时间加上指定时间的响应。 | |
no-store | 用于防止重要的信息被无意的发布。在请求消息中发送将 使得请求和响应消息都不使用缓存,完全不存下來。 | |
no-transform | 主要用于proxy服务器,告诉代理服务器不要随意改动返回的内容 |
Cache-control 案例
// cache-control.html
<html>
<head>
<meta charset="utf-8" />
<title>cache-control</title>
</head>
<body>
<script src="/script.js"></script>
cache
</body>
</html>
// cache-control.js
const http = require('http');
const fs = require('fs');
const port = 3010;
http.createServer((request, response) => {
console.log('request url: ', request.url);
if (request.url === '/') {
const html = fs.readFileSync('cache.html', 'utf-8');
response.writeHead(200, {
'Content-Type': 'text/html',
});
response.end(html);
} else if (request.url === '/script.js') {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=200',
// 'Cache-Control': 'no-cache',
});
response.end("console.log('script load !! -- 1')");
}
}).listen(port);
console.log(`Server started at http://localhost:${port}`)
控制台运营结果: 第一次请求了script.js ,而第二次没有。
指请求了/
并没有请求 /script.js
浏览器结果
image以上结果浏览器并没有返回给我们服务端修改的结果,这是为什么呢?是因为我们请求的url /script.js
没有变,那么浏览器就不会经过服务端的验证,会直接从客户端缓存去读,就会导致一个问题,我们的js静态资源更新之后,不会立即更新到我们的客户端,这也是前端开发中常见的一个问题,我们是希望浏览器去缓存我们的静态资源文件(js、css、img等)我们也不希望服务端内容更新了之后客户端还是请求的缓存的资源, 解决办法也就是我们在做js构建流程时,把打包完成的js文件名上根据它内容hash值加上一串hash码,这样你的js文件或者css文件等内容不变,这样生成的hash码就不会变,反映到页面上就是你的url没有变,如果你的文件内容有变化那么嵌入到页面的文件url就会发生变化,这样就可以达到一个更新缓存的目的,这也是目前前端来说比较常见的一个静态资源方案。
网友评论