浅聊一下
在上一篇文章中(小鹅通一面:被问碎了... - 掘金 (juejin.cn)),有些问题没有详细地解释,在本篇文章作一个补充...
三次握手
三次握手是在TCP协议建立连接时产生的
- 第一次
客户端发送来连接请求到服务器,客户端状态进入 SYN-SENT状态
- 第二次
服务端收到请求连接报文以后,返回一个应答(包含ACK序号),服务端进入 SYN-RECEIVED状态
- 第三次
客户端接收到了同意连接的应答后,还要向服务端发送一个确认收到的报文,再进入ESTABLISHED状态
三次握手能不能少握一次?
答案肯定是不可以的...为什么呢?
假设客户端给服务端发送了一个建立连接请求A,但是因为网络环境差,这个请求A超时了,那么TCP会启动超时重传机制,再发送一个新的建立连接请求B,服务端接收到B请求后应答,如果此时就完成了建立连接的话,当客户端和服务端通信完成后便释放了连接,双方都进入close的状态。假设此时A请求又抵达了服务端,那么服务端会认为客户端又要建立新的连接,从而应答该请求并且进入SYN-RECEIVED的状态,而此时客户端是close的状态,那么服务端就会一直等待造成资源浪费
四次挥手
- 第一次
客户端A认为数据发送完成,向服务端B发送释放连接请求
- 第二次
B收到释放连接请求后,返回一个ACK报文,进入CLOSE_WAIT状态,此时不再接收A发送的数据,但是B仍然可以给A发送数据
- 第三次
B如果此时还有没发完的数据就会继续发送,发完后向A发送释放连接的请求,B进入到LAST-ACK状态
- 第四次
A收到释放连接的请求,向B发送应答,进入CLOSED状态,B接收到该应答也进入CLOSED状态
四次挥手能不能少挥一次?
当然也是不能的,如果只有三次挥手,那么在服务端向客户端发送完数据以后,再发送释放连接的请求,但是客户端不进行应答,服务端认为客户端没收到请求,就一直发送请求,同样的会造成资源浪费...
跨域
说到跨域,那就不得不先说一下同源策略
同源策略
协议号-域名-端口 都相同的地址,浏览器才认为是同源, 这意味着,如果一个网页包含的脚本试图跨域请求其他源的资源,浏览器会阻止这个请求。
同源策略的目的是保护用户的信息安全和隐私。它防止恶意网站通过脚本获取用户的敏感信息,如登录凭证、Cookie 等,并限制了不受信任的脚本对其他网站资源的访问。
解决跨域的几种方法
jsonp
- 原理
ajax请求受同源策略的影响,但是 <script>
上的src属性不受同源策略的影响,且该属性也会导致浏览器发送一个请求
- 演示
index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">获取数据</button>
<script>
let btn = document.getElementById('btn');
function jsonP(url,cb){
return new Promise((resolve,reject)=>{
const script = document.createElement('script');
script.src =`${url}?cb=${cb}` ;
document.body.appendChild(script);
window[cb] = (data)=>{
resolve(data)
}
})
}
btn.addEventListener('click',()=>{
//发请求
jsonP('http://localhost:3000','callback')
.then((res)=>{
console.log('后端的返回结果:'+res)
})
})
</script>
</body>
</html>
- 创建一个
script
元素,并设置其src
属性为传入的URL加上回调函数名作为查询参数。 - 将
script
元素添加到document.body
中,发起请求。 - 在全局作用域下定义一个与回调函数名同名的函数,接收后端返回的数据。
- 使用
Promise
封装异步操作,当后端返回数据时,通过resolve
将数据传递给调用方。 - 在按钮点击事件中调用
jsonP
函数,传入请求URL和回调函数名。 - 通过
.then
方法处理请求成功后的数据,将结果打印到控制台。
app.js
js
const Koa = require('koa');
const app = new Koa();
const main = (ctx,next)=>{
console.log(ctx.query);
const cb = ctx.query.cb;
const data = 'hello world';
const str = `${cb}('${data}')`;
ctx.body = str
}
app.use(main)
app.listen(3000,()=>{
console.log('listen on port 3000');
});
- 借助script的src属性给后端发送一个请求,且携带一个参数('callback')
- 前端在window对象上添加了一个callback函数
- 后端接收到这个参数'callback' 后,将要返回给前端的数据data和这个参数''callback'进行拼接成callback(data)并返回
- 因为window上已经有一个callback函数,后端又返回了一个形如'callback(data)'的调用,浏览器会将该字符串执行成callback的调用。
- 效果
- 缺点
- 必须要后端配合
- 仅支持 GET 请求:由于 JSONP 是通过动态创建
<script>
标签来加载外部脚本,因此只支持 GET 请求。这限制了发送 POST 请求等其他类型的请求。
Cors
- 原理:
后端通过设置响应头来告诉浏览器不要拒绝接收后端的响应
- 演示
在前端向后端的3000端口发送一个请求,要后端返回数据
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">获取数据</button>
<script>
let btn = document.getElementById('btn');
btn.addEventListener('click',()=>{
fetch('http://localhost:3000')
.then((res)=>{
console.log(res.json());
})
.then((res)=>{
console.log(res)
})
})
</script>
</body>
</html>
在后端的响应头中设置白名单,'Access-Control-Allow-Origin':'*',这个*
号代表所有的地址,在项目中应该设置为后端ip地址
js
const http = require('http')
const server = http.createServer((req, res) => {
//跨域是浏览器不接受后端的响应
//想办法让浏览器不得不接收
res.writeHead(200,{
'Access-Control-Allow-Origin':'*'
// 'Access-Control-Allow-Origin':'ip地址'
})
let data = {msg:'Hello cors'}
res.end(JSON.stringify(data))//向前端返回数据
})
server.listen(3000,()=>{
console.log('Server is running on port 3000')
})
- 效果
node代理
使用vite脚手架,配置vite.config.js,就可以实现跨域通信
在App.vue的挂载阶段向后端请求数据
js
import axios from 'axios'
import { onMounted } from 'vue';
onMounted(() => {
axios.get('/api')
.then((res)=>{
console.log(res)
})
})
配置vite.config.js,配置'/api'代表前端直接向'/api'请求数据就等于向localhost:3000端口请求数据,想看具体的掘友可以看(开发服务器选项 | Vite 官方中文文档 (vitejs.dev))
js
export default defineConfig({
plugins: [vue()],
server:{
proxy:{
'/api':{
target:'http://localhost:3000',
changeOrigin:true,
rewrite:(path)=>path.replace(/^\/api/,'')
}
}
}
})
//vite 帮我们启动了一个node服务,帮我们朝'localhost:3000'发送请求,因为
// 后端没有同源策略,所以vite中的node服务能直接请求到数据,再提供给前端使用
vite 帮我们启动了一个node服务,帮我们朝'localhost:3000'发送请求,因为 后端没有同源策略,所以vite中的node服务能直接请求到数据,再提供给前端使用
nginx代理
类似Cors,配置白名单,生产环境下使用
postMessage (在iframe中)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>a.html</h2>
<iframe src="http://127.0.0.1:5500/%E7%99%BE%E5%BA%A6%E9%9D%A2%E8%AF%95%E9%A2%98/postmassage/b.html" frameborder="0" id="iframe"></iframe>
<script>
let iframe = document.getElementById('iframe');
iframe.onload = function(){
let data = {
name:'Tom'
}
iframe.contentWindow.postMessage(JSON.stringify(data),'http://127.0.0.1:5500/%E7%99%BE%E5%BA%A6%E9%9D%A2%E8%AF%95%E9%A2%98/postmassage/b.html')
}
window.addEventListener('message',function(e){
console.log(e.data)
})
</script>
</body>
</html>
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h4>b.html</h4>
<script>
window.addEventListener('message',function(e){
console.log(JSON.parse(e.data))
if(e.data){
setTimeout(function(){
window.parent.postMessage('我接收到','http://127.0.0.1:5500')
},2000)
}
})
</script>
</body>
</html>
一个页面中图片的在线地址是否做了跨域处理?
很显然,一个图片的url和前端的是不同源的,那么是否代表这个图片已经做过了跨域处理呢?
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="https://img.zcool.cn/community/01df7b56de44db6ac72531cb2906b9.JPG@1280w_1l_2o_100sh.jpg" alt="">
</body>
</html>
其实img标签的src属性和script标签的src属性是一样的,不受同源策略的影响,所以这里是不用做同源处理的...
结尾
总结完毕,接着面...