文章目录
- 一、HTTP学习
- 二、TCP/IP学习
- 2.1 TCP/IP 简介
- 2.2 TCP/IP 协议
- 2.2.1 TCP - 传输控制协议
- 2.2.2 IP - 网际协议(Internet Protocol)
- 2.2.3 HTTP - 超文本传输协议(Hyper Text Transfer Protocol)
- 2.2.4 HTTPS - 超文本传输安全协议(HTTP Secure)
- 2.2.5 SSL - 安全套接字层(Secure Sockets Layer)
- 2.2.6 SMTP - 简易邮件传输协议(Simple Mail Transfer Protocol)
- 2.2.7 MIME - 多用途因特网邮件扩展(Multi-purpose Internet Mail Extensions)
- 2.2.8 IMAP - 因特网消息访问协议(Internet Message Access Protocol)
- 2.2.9 POP - 邮局协议(Post Office Protocol)
- 2.2.10 FTP - 文件传输协议(File Transfer Protocol)
- 2.2.11 DHCP - 动态主机配置协议(Dynamic Host Configuration Protocol)
- 2.2.12 ARP - 地址解析协议(Address Resolution Protocol)
- 2.2.13 RARP - 反向地址转换协议(Reverse Address Resolution Protocol)
- 2.3 TCP/IP 模型
- 2.4 TCP协议的三次握手和四次挥手
- 三、UDP协议
- 四、其他网络知识
- 五、OSI七层模型
- 六、WebSocket
- 7.SSE服务器发送事件(Server-Sent Events)
一、HTTP学习
1.1 HTTP 简介
HTTP协议
(HyperText Transfer Protocol,超文本传输协议
)是因特网上应用最为广泛的一种网络传输协议
。是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP
是一个基于TCP/IP
通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。是TCP/IP协议的一个应用层
协议
1.1.1 HTTP 工作原理
HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。
Web服务器有:Apache服务器,IIS服务器(Internet Information Services)等。
Web服务器根据接收到的请求后,向客户端发送响应信息。
HTTP默认端口号为80,但是你也可以改为8080或者其他端口。
1.1.2 HTTP 注意事项
HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
1.2 HTTP 消息结构
HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。
一个HTTP"客户端"是一个应用程序(Web浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个HTTP的请求的目的。
一个HTTP"服务器"同样也是一个应用程序(通常是一个Web服务,如Apache Web服务器或IIS服务器等),通过接收客户端的请求并向客户端发送HTTP响应数据。
HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。
一旦建立连接后,数据消息就通过类似Internet邮件所使用的格式[RFC5322]和多用途Internet邮件扩展(MIME)[RFC2045]来传送。
1.2.1 客户端请求消息
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
1.2.1.1 请求行
请求行包括请求URL(Request URL)、请求方法(Request Method)、
1.2.1.2 请求头
请求携带的一些规范要求信息,可以手动写入请求头
// 请求拦截器中
axios.interceptors.request.use(function (request) {
/**
* desc: 携带请求头
* language 中文,英文
* @params
**/
if (setting.language === "zh-CN" || setting.language === "zh") {
request.headers["language"] = "zh_CN";
} else if (setting.language === "en_US" || setting.language === "en") {
request.headers["language"] = "en_US";
}
}
1.2.1.3 请求数据
接口请求的主要数据
1.2.2 服务器响应消息
HTTP响应也由四个部分组成,分别是:状态行、响应头、空行和响应正文。
1.3 HTTP 请求方法
根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
1.3.1 POST 与 GET 的区别
- Get是从服务器上获取数据,Post是向服务器传送数据。
- Get是把参数数据队列加到提交表单的Action属性所指向的URL中,值和表单内各个字段一一对应,在URL中可以看到。
- Get传送的数据量小,不能大于2KB;Post传送的数据量较大,一般被默认为不受限制。
- 根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。
1.3 HTTP 状态码
当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。
下面是常见的HTTP状态码:
- 200 - 请求成功
- 301 - 资源(网页等)被永久转移到其它URL
- 400 客户端请求的语法错误,服务器无法理解
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
- 502 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
1.4 HTTP浏览器缓存
HTTP 缓存是前端性能优化的重要手段,通过减少网络请求和加快资源加载来提升用户体验。
1.4.1 缓存分类
HTTP 缓存分为 强缓存
和 协商缓存
,优先级较高的是强缓存:
- 强缓存:直接使用本地缓存,不发送请求到服务器。
- 协商缓存:发送请求到服务器,由服务器判断是否使用缓存。
1.4.2 强缓存
通过 Cache-Control
和 Expires
响应头控制:
- Cache-Control(HTTP/1.1,优先级更高):
- max-age=3600:资源有效期为 3600 秒。
- public:允许代理服务器缓存。
- private:仅允许浏览器缓存。
- no-cache:强制走协商缓存。
- no-store:禁止缓存。
- Expires(HTTP/1.0):指定过期时间(绝对时间),受本地时间影响可能不准。
触发条件:
浏览器请求某一资源时,会先获取该资源缓存的header
信息,然后根据header
中的Cache-Control
和Expires
来判断是否过期。若没过期则直接从缓存中获取资源信息,包括缓存的header的信息,所以此次请求不会与服务器进行通信。
1.4.3 协商缓存
当强缓存失效时,浏览器携带缓存标识向服务器验证,服务器返回 304 Not Modified
时使用缓存。
标识字段:
- Last-Modified & If-Modified-Since:
- 服务器返回 Last-Modified(资源最后修改时间)。
- 浏览器下次请求带上 If-Modified-Since,服务器对比时间决定是否返回 304。
- 缺点:精度到秒,频繁修改可能无法识别。
- ETag & If-None-Match(优先级更高):
- 服务器返回 ETag(资源唯一标识,如哈希值)。
- 浏览器下次请求带上 If-None-Match,服务器对比 ETag 决定是否返回 304。
- 解决精度和分布式服务器时间同步问题。
Last-Modified
是一个时间戳,如果我们启用了协商缓存
,它会在首次请求时随着 Response Headers
返回:每次请求去判断这个时间戳是否发生变化。从而去决定你是304读取缓存还是给你返回最新的数据。
服务器会优先验证ETag
Etag/If-None-Match
返回的是一个校验码。ETag
可以保证每一个资源是唯一的,资源变化都会导致ETag
变化。服务器根据浏览器上送的If-None-Match
值来判断是否命中缓存。与Last-Modified
不一样的是,当服务器返回304 Not Modified的响应时,由于ETag
重新生成过,response header
中还会把这个ETag
返回,即使这个ETag
跟之前的没有变化。
普通刷新会启用弱缓存,忽略强缓存。只有在地址栏或收藏夹输入网址、通过链接引用资源等情况下,浏览器才会启用强缓存,这也是为什么有时候我们更新一张图片、一个js文件,页面内容依然是旧的,但是直接浏览器访问那个图片或文件,看到的内容却是新的。
1.4.4 缓存流程
- 浏览器请求资源,先检查强缓存(Cache-Control/Expires)。
- 若命中强缓存,直接使用本地资源。
- 若未命中,发送请求到服务器,携带 If-Modified-Since 或 If-None-Match。
- 服务器验证资源是否变更:
- 未变更 → 返回 304,浏览器使用缓存。
- 已变更 → 返回 200 和新资源。
1.4.5 实际应用建议
- 静态资源(JS/CSS/图片):
- 设置强缓存(如 Cache-Control: max-age=31536000)。
- 通过文件名哈希(main.a1b2c3.js)实现内容变更后缓存失效。
- HTML 文件:
- 使用 no-cache 或短 max-age,确保及时更新。
1.4.6 常见面试问题
- Q:no-cache 和 no-store 的区别?
- no-cache:可缓存,但每次需向服务器验证。
- no-store:禁止缓存,直接请求新资源。
- Q:为什么有了
Last-Modified
还需要ETag
?- 解决精度不足(秒级)、分布式服务器时间不一致等问题。
- Q:如何避免用户看到旧版本?
- 静态资源用哈希命名,HTML 文件禁用强缓存。
二、TCP/IP学习
2.1 TCP/IP 简介
TCP/IP 是供已连接因特网的计算机进行通信的通信协议。
TCP/IP 指传输控制协议/网际协议
TCP/IP 定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准。
计算机通信协议是对那些计算机必须遵守以便彼此通信的的规则的描述。
2.2 TCP/IP 协议
TCP/IP 是基于 TCP 和 IP 这两个最初的协议之上的不同的通信协议的大集合。
2.2.1 TCP - 传输控制协议
TCP 用于从应用程序到网络的数据传输控制。
TCP 负责在数据传送之前将它们分割为 IP 包,然后在它们到达的时候将它们重组。
2.2.2 IP - 网际协议(Internet Protocol)
IP 负责计算机之间的通信。
IP 负责在因特网上发送和接收数据包。
2.2.3 HTTP - 超文本传输协议(Hyper Text Transfer Protocol)
HTTP 负责 web 服务器与 web 浏览器之间的通信。
HTTP 用于从 web 客户端(浏览器)向 web 服务器发送请求,并从 web 服务器向 web 客户端返回内容(网页)。
2.2.4 HTTPS - 超文本传输安全协议(HTTP Secure)
HTTPS 负责在 web 服务器和 web 浏览器之间的安全通信。
HTTPS 经由 HTTP 进行通信,但利用 SSL/TLS 来加密数据包。HTTPS 开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。
2.2.5 SSL - 安全套接字层(Secure Sockets Layer)
SSL 协议用于为安全数据传输加密数据。
2.2.6 SMTP - 简易邮件传输协议(Simple Mail Transfer Protocol)
SMTP 用于电子邮件的传输。
2.2.7 MIME - 多用途因特网邮件扩展(Multi-purpose Internet Mail Extensions)
MIME 协议使 SMTP 有能力通过 TCP/IP 网络传输多媒体文件,包括声音、视频和二进制数据。
2.2.8 IMAP - 因特网消息访问协议(Internet Message Access Protocol)
IMAP 用于存储和取回电子邮件。
2.2.9 POP - 邮局协议(Post Office Protocol)
POP 用于从电子邮件服务器向个人电脑下载电子邮件。
2.2.10 FTP - 文件传输协议(File Transfer Protocol)
FTP 负责计算机之间的文件传输。
2.2.11 DHCP - 动态主机配置协议(Dynamic Host Configuration Protocol)
DHCP 用于向网络中的计算机分配动态 IP 地址。
2.2.12 ARP - 地址解析协议(Address Resolution Protocol)
ARP - 用于通过 IP 来查找基于 IP 地址的计算机网卡的硬件地址。
2.2.13 RARP - 反向地址转换协议(Reverse Address Resolution Protocol)
RARP 用于通过 IP 查找基于硬件地址的计算机网卡的 IP 地址。
2.3 TCP/IP 模型
2.4 TCP协议的三次握手和四次挥手
SEQ
:"sequance"序列号;ACK
:"acknowledge"确认号;SYN
:"synchronize"请求同步标志;ACK
:“acknowledge"确认标志”;FIN
:"Finally"结束标志
TCP连接建立过程:
- 首先Client端
发送连接请求报文
, - Server段接受连接后
回复ACK报文
,并为这次连接分配资源。 - Client端接收到ACK报文后也向Server段
发送ACK报文
,并分配资源,这样TCP连接就建立了。
TCP连接断开过程:
- Client端发起中断连接请求,
发送FIN报文
。 - Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先
发送ACK
,“告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT状态
,继续等待Server端的FIN报文。 - 当Server端确定数据已发送完成,则向Client端
发送FIN报文
,“告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。 - Client端收到FIN报文后,“就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以
发送ACK
后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。”,Server端收到ACK后,“就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
2.4.1 为什么要三次握手?
在只有两次"握手"的情形下,假设Client想跟Server建立连接,但是却因为中途连接请求的数据报丢失了,故Client端不得不重新发送一遍;这个时候Server端仅收到一个连接请求,因此可以正常的建立连接。但是,有时候Client端重新发送请求不是因为数据报丢失了,而是有可能数据传输过程因为网络并发量很大在某结点被阻塞了,这种情形下Server端将先后收到2次请求,并持续等待两个Client请求向他发送数据…问题就在这里,Cient端实际上只有一次请求,而Server端却有2个响应,极端的情况可能由于Client端多次重新发送请求数据而导致Server端最后建立了N多个响应在等待,因而造成极大的资源浪费!所以,"三次握手"很有必要!
2.4.2 为什么要四次挥手?
试想一下,假如现在你是客户端你想断开跟Server的所有连接该怎么做?第一步,你自己先停止向Server端发送数据,并等待Server的回复。但事情还没有完,虽然你自身不往Server发送数据了,但是因为你们之前已经建立好平等的连接了,所以此时他也有主动权向你发送数据;故Server端还得终止主动向你发送数据,并等待你的确认。其实,说白了就是保证双方的一个合约的完整执行!
使用TCP的协议:FTP(文件传输协议)、Telnet(远程登录协议)、SMTP(简单邮件传输协议)、POP3(和SMTP相对,用于接收邮件)、HTTP协议等。
三、UDP协议
UDP用户数据报协议,是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。
UDP与TCP位于同一层,但它不管数据包的顺序、错误或重发。
使用UDP协议包括:TFTP(简单文件传输协议)、SNMP(简单网络管理协议)、DNS(域名解析协议)、NFS、BOOTP。
TCP 与 UDP 的区别:TCP是面向连接的,可靠的字节流服务;UDP是面向无连接的,不可靠的数据报服务。
四、其他网络知识
4.1 在浏览器上输入网址后的全部过程
- 读取缓存: 搜索自身的 DNS 缓存。
- DNS解析:客户端浏览器通过DNS解析到www.baidu.com的IP地址220.181.27.48,通过这个IP地址找到客户端到服务器的路径。
- TCP 连接:客户端浏览器发起一个HTTP会话到220.161.27.48,然后通过TCP进行封装数据包,输入到网络层。
- 在客户端的传输层,把HTTP会话请求分成报文段,添加源和目的端口,如服务器使用80端口监听客户端的请求,客户端由系统随机选择一个端口如5000,与服务器进行交换,服务器把相应的请求返回给客户端的5000端口。然后使用IP层的IP地址查找目的端。
- 客户端的链路层,包通过链路层发送到路由器,通过邻居协议查找给定IP地址的MAC地址,然后发送ARP请求查找目的地址,如果得到回应后就可以使用ARP的请求应答交换的IP数据包现在就可以传输了,然后发送IP数据包到达服务器的地址。
- 浏览器解析渲染页面:根据 HTML 解析出 DOM 树、根据 CSS 解析生成 CSS 规则树、结合 DOM 树和 CSS 规则树,生成渲染树、根据渲染树计算每一个节点的信息、根据计算好的信息绘制页面
- 断开连接:TCP 四次挥手
4.2 HTTP 与 HTTPS 的区别
- HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
- 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。
- HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
- http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
- HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
4.3 什么是跨域
跨域的本质就是网页页面所存在的服务器中的前端代码想要访问其他的域名所存在的服务器。
同源策略:就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
因为浏览器出于安全考虑,有同源策略。如果协议、域名或者端口有一个不同就是跨域,异步请求会失败。
同源策略是为了防止 CSRF(跨站请求伪造)
CSRF 攻击通过在授权用户访问的页面中包含链接或者脚本的方式工作。
4.3.1 跨域解决方法
4.3.1.1 JSONP
JSONP(填充式JSON),应用JSON的一种新方法
JSON、JSONP的区别:
- JSON返回的是一串数据、JSONP返回的是脚本代码(包含一个函数调用)
- JSONP 只支持get请求、不支持post请求(类似往页面添加一个script标签,通过src属性去触发对指定地址的请求,故只能是Get请求)
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求
核心思想:网页通过添加一个<script>
元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
// 处理服务器返回回调函数的数据
<script type="text/javascript">
function dosomething(res){
// 处理获得的数据
console.log(res.data)
}
</script>
或者
//原生的实现方式
let script = document.createElement('script');
script.src = 'http://www.nealyang.cn/login?username=Nealyang&callback=callback';
document.body.appendChild(script);
function callback(res) {
console.log(res);
}
json文件中的格式
// 回调函数 名称要与 文件中的名称一致
indexDemo({
"a":"lllll",
"b":"2222"
})
4.3.1.2 CORS 跨域资源共享
CORS 是跨域资源共享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
两种请求:
CORS分为两种请求,一种是简单请求,另一种是非简单请求。
4.3.1.2.1 CORS 简单请求
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0
...
Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。 浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
- Access-Control-Allow-Origin :该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
- Access-Control-Allow-Credentials: 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
- Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
4.3.1.2.2 CORS 非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
- Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
- Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header
预检请求的回应
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。
服务器回应的其他CORS相关字段如下:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
- Access-Control-Allow-Methods:该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
- Access-Control-Allow-Headers:如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
- Access-Control-Allow-Credentials: 该字段与简单请求时的含义相同。
- Access-Control-Max-Age: 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
浏览器正常请求回应
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
浏览器的正常CORS请求。上面头信息的Origin字段是浏览器自动添加的。下面是服务器正常的回应。
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin字段是每次回应都必定包含的
4.3.1.3 node代理跨域
node中间件实现跨域代理,是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use('/', proxy({
// 代理跨域目标接口
target: 'http://www.domain2.com:8080',
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
res.header('Access-Control-Allow-Credentials', 'true');
},
// 修改响应信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}));
app.listen(3000);
console.log('Proxy server is listen at port 3000...');
4.3.1.4 nginx代理跨域
4.3.1.4.1 nginx配置解决iconfont跨域
浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。
location / {
add_header Access-Control-Allow-Origin *;
}
4.3.1.4.2 nginx反向代理接口跨域
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
nginx具体配置:
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
1.) 前端代码示例:
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
2.) Nodejs后台示例:
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var params = qs.parse(req.url.substring(2));
// 向前台写cookie
res.writeHead(200, {
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取
});
res.write(JSON.stringify(params));
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
4.4 SessionStorage、localStorage和cookie的区别
- 数据传递与存储位置:
Cookie
: HTTP请求时会携带cookie,即cookie在浏览器和服务器间来回传递。SessionStorage
:仅在当前浏览器窗口或标签页中存储数据,且不会将数据发送给服务器。LocalStorage
:同样在本地保存数据,但不会将数据发送给服务器,并且可以在所有同源窗口或标签页中共享。
- 存储大小限制:
Cookie
:数据大小限制为4K,因此只适合保存小数据,如会话标识。SessionStorage
和LocalStorage
:存储大小限制通常比cookie大得多,可以达到5M或更大。
- 数据有效期:
Cookie
:只在设置的cookie过期时间之前有效,过期时间可以由服务器设置。SessionStorage
:仅在当前浏览器窗口或标签页关闭之前有效。LocalStorage
:始终有效,即使窗口或浏览器关闭也会一直保存,因此适合存储持久数据。
- 作用域:
Cookie
:在所有同源窗口中都是共享的。SessionStorage
:不在不同的浏览器窗口中共享,即使是同一个页面。在原窗口点击超链接或者window.open打开页面会继承原先的SessionStorage。LocalStorage
:同样在所有同源窗口中都是共享的。
4.5 前端性能优化方案
从三个方面来说一下前端优化
一、webapck优化与开启gzip压缩
- babel-loader用 include 或 exclude 来帮我们避免不必要的转译,不转译node_moudules中的js文件。其次在缓存当前转译的js文件。
- 文件采用按需加载
- gzip压缩
- 图片优化,采用svg图片或者字体图标
- 浏览器缓存机制
二、本地存储
- SessionStorage
- localStorage
- cookie
三、代码优化
- 事件的节流和防抖
- 页面的回流和重绘
- EventLoop事件循环机制
- 代码优化等等
五、OSI七层模型
一般叫做OSI(Open System Interconnection) 模型
或者叫七层模型
.他是国际标准化(ISO)定的一个用于计算机或通信系统间互联的标准体系.
协议将计算机网络体系结构划分为7层
.
5.1 各层定义
-
应用层
OSI参考模型中最靠近用户的一层,是为计算机用户提供应用接口,也为用户直接提供各种网络服务。我们常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP等。 -
表示层
表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。 -
会话层
会话层就是负责建立、管理和终止表示层实体之间的通信会话
。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。 -
传输层
传输层建立了主机端到端的链接
,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。我们通常说的,TCP UDP就是在这一层。端口号既是这里的“端”。 -
网络层
本层通过IP寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点
,正确无误地按照地址传送给目的端的运输层。就是通常说的IP层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础。 -
数据链路层
将比特组合成字节,再将字节组合成帧
,使用链路层地址 (以太网使用MAC地址)来访问介质,并进行差错检测
。
数据链路层又分为2个子层:逻辑链路控制子层(LLC)和媒体访问控制子层(MAC)。
MAC子层处理CSMA/CD算法、数据出错校验、成帧等;LLC子层定义了一些字段使上次协议能共享数据链路层。 在实际使用中,LLC子层并非必需的。 -
物理层
实际最终信号的传输是通过物理层实现的。通过物理介质传输比特流
。规定了电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。
六、WebSocket
WebSocket
是一种浏览器与服务器进行全双工通讯
的网络技术,属于应用层协议
。它基于TCP传输协议,并复用HTTP的握手通道。
最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息
- 支持双向通信,实时性更强。
- 可以发送文本,也可以发送二进制数据。
- 数据格式比较轻量,性能开销小,通信高效。
- 没有同源限制,客户端可以与任意服务器通信。
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
6.1 WebSocket 与 HTTP 的区别
相同点: 都是一样基于TCP的,都是可靠性传输协议。都是应用层协议。
联系: WebSocket
在建立握手时,数据是通过HTTP
传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。
6.2 WebSocket连接的过程
-
首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
-
然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
-
最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。
6.3 Websocket 优缺点
优点:
- WebSocket协议一旦建议后,互相沟通所消耗的请求头是很小的
- 服务器可以向客户端推送消息了
缺点:
- 少部分浏览器不支持,浏览器支持的程度与方式有区别(IE10)
6.4 Websocket 断线重连
心跳
就是客户端定时的给服务端发送消息,证明客户端是在线的, 如果超过一定的时间没有发送则就是离线了。
1.判断是否离线
- 当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中,
- 第二次客户端定时再次发送请求依旧携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,
- 得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线;
2.解决断线问题
- 修改nginx配置信息
- websocket发送心跳包
心跳检测步骤:
- 客户端每隔一个时间间隔发生一个探测包给服务器
- 客户端发包时启动一个超时定时器
- 服务器端接收到检测包,应该回应一个包
- 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
- 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
6.5 WebSocket应用场景
- 在线协同编辑/编辑
- 实时数据流的拉取与推送
- 体育/游戏实况
- 实时地图位置
- 即时Web应用程序: 例如在交易网站,它用于显示价格波动,数据被后端服务器使用Web套接字通道连续推送到客户端。
- 游戏应用程序:屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在WebSocket游戏应用程序中非常有帮助。
- 聊天应用程序:聊天应用程序仅使用WebSocket建立一次连接,便能在订阅户之间交换,发布和广播消息。它重复使用相同的WebSocket连接,用于发送和接收消息以及一对一的消息传输。
6.6 demo实现
用 ws 搭建起来的服务端
const { WebSocketServer } = require('ws')
const wss = new WebSocketServer({
port: 7070
})
wss.on('connection', (ws, req) => {
console.log('客户端已连接:', req.socket.remoteAddress)
ws.on('message', data => {
console.log('收到客户端发送的消息:', data)
})
setInterval(()=>{
ws.send(`我是服务端${Math.random()}`) // 向当前客户端发送消息
},1000)
})
react前端通过new WebSocket('ws://localhost:7070')
开启连接
- WebSocket.send() 发送会话
- WebSocket.close() 关闭会话
- WebSocket.onmessage() 接受会话
const webSocketInit = useCallback(() => {
const stateArr = [
'正在链接中',
'已经链接并且可以通讯',
'连接正在关闭',
'连接已关闭或者没有链接成功',
];
if (!ws.current || ws.current.readyState === 3) {
ws.current = new WebSocket('ws://localhost:7070');
ws.current.onopen = _e =>
setReadyState(stateArr[ws.current?.readyState ?? 0]);
ws.current.onclose = _e =>
setReadyState(stateArr[ws.current?.readyState ?? 0]);
ws.current.onerror = e =>
setReadyState(stateArr[ws.current?.readyState ?? 0]);
ws.current.onmessage = e => {
setMessage(e.data);
};
}
}, [ws]);
/**
* 初始化 WebSocket
* 且使用 WebSocket 原声方法获取信息
* */
useLayoutEffect(() => {
getRandomInt();
webSocketInit();
return () => {
ws.current?.close();
};
}, [ws, getRandomInt, webSocketInit]);
6.7 Vue和WebSocket实现的讨论区
<template>
<div>
<h1>讨论区</h1>
<div v-for="message in messages" :key="message.id">
{{ message.content }}
</div>
<div>
<input v-model="newMessage" @keyup.enter="sendMessage" placeholder="输入消息...">
<button @click="sendMessage">发送</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
newMessage: '',
messages: [],
socket: null,
};
},
created() {
this.socket = this.$socket; // 这是vue-socket.io提供的便捷方式来获取socket实例
this.socket.on('connect', () => {
console.log('WebSocket connected');
});
this.socket.on('receive message', (data) => {
this.messages.push(data);
});
},
methods: {
sendMessage() {
this.socket.emit('send message', { id: Date.now(), content: this.newMessage });
this.newMessage = ''; // 清空输入框
},
},
};
</script>
这个例子中,当用户在输入框中输入内容并点击发送按钮或按回车键时,消息会被发送到服务器。服务器会将消息广播给所有连接的客户端,客户端监听这个事件并更新UI。
7.SSE服务器发送事件(Server-Sent Events)
- HTML5
服务器发送事件(server-sent event)
允许网页获得来自服务器的更新。 - 通常用于创建单向连接,使服务器能够发送任意数量的数据
Server-Sent
事件 - 单向消息传递EventSource
对象用于接收服务器发送事件通知
以下是使用SSE的基本步骤:
1.创建一个EventSource
对象。这可以通过指定URL来完成,例如:
var es = new EventSource('demo_sse.php');
onopen
当通往服务器的连接被打开onmessage
当接收到消息onerror
当发生错误
2.添加事件处理方法。可以通过onmessage
和addEventListener
方法来添加事件处理方法。当服务器端有新的事件产生时,相应的事件处理方法会被调用。例如:
es.onmessage = function(event) {
console.log(event.data);
};
es.addEventListener('myevent', function(event) {
console.log(event.data);
});
3.保持连接。一旦建立了EventSource
对象,浏览器会不断地访问服务器中的数据,直到连接被关闭。这个过程可能会有延迟,具体取决于服务器类型、数据大小和浏览器支持情况等因素。
需要注意的是,SSE只支持从服务器到客户端的单向通信,不支持客户端向服务器的请求或消息传递。此外,SSE不支持跨域请求,因此在使用时需要确保服务器设置了适当的CORS(跨来源资源共享)策略。
Vue和SSE的简单示例
<template>
<div>
<h1>Server-Sent Events Example</h1>
<div v-html="message"></div>
</div>
</template>
<script>
export default {
data() {
return {
message: ''
}
},
created() {
let es = new EventSource('/api/sse');
es.onmessage = function(event) {
this.message = event.data;
}
}
}
</script>
在这个例子中,我们创建了一个Vue
组件,该组件在创建时使用EventSource
对象连接到SSE
服务。当接收到服务器端发送的新消息时,onmessage
事件处理程序将消息数据设置为组件的message
数据属性。然后,Vue
将自动更新DOM以反映新的数据。
SSE与WebSocket的区别
SSE
(Server-Sent Events)和WebSocket
都是实现服务器与客户端实时通信的技术,但它们之间存在一些重要的差异。
- 通信模式:
SSE
是一种单向的通信模式,只能由服务器向客户端推送数据。而WebSocket
是双向通信模型,客户端和服务器可以互相发送消息。 - 复杂性:相对于
WebSocket
,SSE
是一个轻量级协议,相对简单。WebSocket
则是一种较重的协议,相对复杂。 - 兼容性和部署:
SSE
是部署在HTTP
协议之上的,现有的服务器软件都支持。而WebSocket
需要服务器端支持新的协议。 - 断线重连:
SSE
默认支持断线重连,而WebSocket
则需要额外的配置。 - 数据格式:
SSE
不支持自定义发送的数据类型,而WebSocket
可以处理更复杂的通信需求,如自定义消息格式、心跳检测、连接状态管理等。
因此,在选择使用SSE
还是WebSocket
时,需要根据具体的应用需求来决定。如果只需要从服务器向客户端推送数据,并且希望简化实现和部署过程,那么SSE
可能是一个更好的选择。如果需要更复杂的双向通信,或者需要处理更复杂的数据格式和通信需求,那么WebSocket
可能更适合。