三次握手四次挥手以及跨域问题面试题详解

浅聊一下

在上一篇文章中(小鹅通一面:被问碎了... - 掘金 (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>
  1. 创建一个script元素,并设置其src属性为传入的URL加上回调函数名作为查询参数。
  2. script元素添加到document.body中,发起请求。
  3. 在全局作用域下定义一个与回调函数名同名的函数,接收后端返回的数据。
  4. 使用Promise封装异步操作,当后端返回数据时,通过resolve将数据传递给调用方。
  5. 在按钮点击事件中调用jsonP函数,传入请求URL和回调函数名。
  6. 通过.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');
});
  1. 借助script的src属性给后端发送一个请求,且携带一个参数('callback')
  2. 前端在window对象上添加了一个callback函数
  3. 后端接收到这个参数'callback' 后,将要返回给前端的数据data和这个参数''callback'进行拼接成callback(data)并返回
  4. 因为window上已经有一个callback函数,后端又返回了一个形如'callback(data)'的调用,浏览器会将该字符串执行成callback的调用。
  • 效果
  • 缺点
  1. 必须要后端配合
  2. 仅支持 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属性是一样的,不受同源策略的影响,所以这里是不用做同源处理的...

结尾

总结完毕,接着面...

相关推荐
理想不理想v9 分钟前
vue经典前端面试题
前端·javascript·vue.js
不收藏找不到我10 分钟前
浏览器交互事件汇总
前端·交互
YBN娜24 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=24 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck29 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。1 小时前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼1 小时前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端