前端面试练习24.3.6

前言:

今天就是复习回顾一下websocket,之前有个AI项目使用到了这个,有点久远了,回顾一下大致过程和难点。

一些知识点:

1.单工,半双工,全双工

单工:

数据只能沿着一个方向传递,例如电视广播等。

优点:实现简单

缺点:传输效率低

半双工:

数据可以在双方之间进行传递,但是不能同时进行,必须有个 发送/接收 角色的转换,一方发送完成另一方才能发送。比如对讲机。

全双工:

数据可以同时在两个方向上传输,通信的两个设备之间可以同时的收发消息,无需等待对方完成,极大地提高了数据的传输效率,典型用例就是电话。

2.websocket可能遇到的难点

参考WebSocket项目中难点与解决方法_websocket客服聊天 难点-CSDN博客

难点一:连接的建立与保持

1.采用连接池

WebSocket初始连接负担较大,主要体现在频繁的连接建立和保持连接的开销较高。

采用连接池,服务端解决

引入了websocket-pool库,通过维护连接池,成功实现了连接的复用。这极大地降低了频繁建立和关闭连接的开销,提升了性能。

2.心跳机制引入

实施了定时的心跳机制,周期性地向服务器发送心跳消息,确保连接保持活跃。这有效防止了连接被自动关闭,提高了连接的可靠性。

3.长连接,适当延长连接时常

比如在连接时,设置超时时长5秒,5秒内没有得到服务器回应,则出现超时提示​​​​​​​

难点二:错误处理与断线重连

1.错误处理

监听onerror事件,当 WebSocket 发生错误时,执行回调函数捕获错误信息​​​​​​​

2.短线重连

自动断线重连机制,采用了指数退避算法。通过逐渐增加重连的间隔时间,我们成功避免了频繁尝试重新建立连接,确保了连接的稳定性和用户体验。

难点三:性能的一些优化

1.消息压缩

目前还没解决

2.并发限制

连接池设置最大数量

3.消息队列

目前没解决

websocket实战:

服务端创建websocket服务

创建node项目,安装相关依赖

npm init -y
npm install express
npm install ws
javascript 复制代码
const WebSocket = require('ws');
const PORT = 8080;
const URL = 'ws://127.0.0.1:8080';
const wss = new WebSocket.Server({ port: PORT });
const clients = new Map();


wss.on('connection', function connection(ws) {
  console.log('Client connected');

  ws.on('message', function incoming(message) {
    const data = JSON.parse(message);
    if (typeof data !== 'string') {
      
      let targetUser = data.toUser

      // 如果客户端之前没有存储在 clients Map 中,则存储客户端连接
      if (!clients.has(data.user)) {
        clients.set(data.user, ws);
      }
      console.log(clients.keys())

      // 将格式化后的消息发送给客户端
      const formattedMessage = `${data.user}:  - ${data.time}<br>${data.message}`;

      if (clients.has(targetUser)) {
        ws.send(formattedMessage);
        const clientSocket = clients.get(targetUser);

        // 检查连接是否已经打开
        if (clientSocket.readyState === WebSocket.OPEN) {
          // 发送消息给目标用户
          clientSocket.send(formattedMessage);
        } else {
          clientSocket.send(`连接到用户 ${targetUser} 的WebSocket已关闭。`);
        }
      } else {
        ws.send(`用户 ${targetUser} 不在线或未连接到WebSocket服务器。`);
      }
      console.log('Received:', formattedMessage);
    }


  });

  // 当完成后释放连接回连接池
  // webSocketPool.releaseConnection(connection);
});

console.log('WebSocket server is running on port 8080');
console.log(`WebSocket 服务器正在监听 127.0.0.1:${PORT}`);

客户端编写连接代码

html 复制代码
<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <title>前端学习</title>
  <style>
    * {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>

  写一个聊天显示的窗口,以及信息发送输入框和发送按钮
  <div id='chat' style="display: flex;justify-content: space-around;">
    <div class="box1">
      <div id='user1_messages' style="width: 600px;height: 300px;border:1px solid black">
        <h1>
          WebSocket1 user:<span id="user1_h1" style="color: rgb(232, 154, 66);"></span>
        </h1>
      </div>
      <input type='text' id='messageInput1' autocomplete='off' placeholder='Type your message here...'>
      <button id='sendButton1'>Send</button>
    </div>

    <div class="box2">
      <div id='user2_messages' style="width: 600px;height: 300px;border:1px solid black">
        <h1>
          WebSocket2 user:<span id="user2_h1" style="color: rgb(232, 154, 66);"></span>
        </h1>
      </div>
      <input type='text' id='messageInput2' autocomplete='off' placeholder='Type your message here...'>
      <button id='sendButton2'>Send</button>
    </div>

  </div>

  <script type='text/javascript'>
    let data = [
      {
        user: 'xhc',
        message: '',
        time: '',
        toUser: ''
      }, {
        user: 'slj',
        message: '',
        time: '',
        toUser: ''
      },
      {
        user: 'aaa',
        message: '',
        time: '',
        toUser: ''
      }
    ]

    let mess1 = document.getElementById('user1_messages');
    let mess2 = document.getElementById('user2_messages');
    let user1_h1 = document.getElementById('user1_h1');
    user1_h1.innerHTML = `${data[0].user}`

    let input1 = document.getElementById('messageInput1');
    let input2 = document.getElementById('messageInput2');
    let user2_h1 = document.getElementById('user2_h1');
    user2_h1.innerHTML = `${data[1].user}`

    let send1 = document.getElementById('sendButton1');
    let send2 = document.getElementById('sendButton2');

    const URL = 'ws://localhost:8080'

    const connect1 = () => {
      // 创建 WebSocket 连接
      const socket = new WebSocket(URL);
      let timer; // Variable to store the interval
    
      // 当连接打开时执行的操作
      socket.onopen = function (event) {
        console.log('WebSocket1 连接已建立');
        data[0].message = input1.value;
        data[0].time = new Date().toLocaleString();
        data[0].toUser = 'slj';
        // 发送消息到服务器
        socket.send(JSON.stringify(data[0]));
        input1.value = '';
    
        // 设置定时器,每隔30秒执行一次指定的回调函数
        timer = setInterval(() => {
          // 检查 WebSocket 连接的当前状态是否为 OPEN
          if (socket.readyState === WebSocket.OPEN) {
            // 如果连接处于 OPEN 状态,则通过连接对象发送心跳消息 'keep-alive'
            socket.send(JSON.parse('keep-alive'));
          }
        }, 30000); // 每 30 秒发送一次心跳
      };
    
      // 当接收到消息时执行的操作
      socket.onmessage = function (event) {
        let reply = event.data;
        mess1.innerHTML = `${mess1.innerHTML} <br>${reply}`;
    
        console.log('来自服务器的消息1:', reply);
      };
    
      // 当连接关闭时执行的操作
      socket.onclose = function (event) {
        mess1.innerHTML = 'WebSocket1 连接已关闭';
        console.log('WebSocket1 连接已关闭');
        // 清除定时器
        clearInterval(timer);
      };
    };
    
    send1.addEventListener('click', connect1)


    let reconnectDelay = 2000; // 初始重连延迟为 2 秒
    let reconnectCount = 0; // 当前重连次数
    const maxReconnectCount = 7; // 最大重连次数为 7
    const maxReconnectDelay = 60000; // 最大重连延迟为 60 秒

    const connect2 = () => {
      let socket; // WebSocket 连接对象
      let timer; // 用于心跳的定时器
      let timeoutTimer; // 连接超时的定时器
     
    
      // 定义超时操作函数
      function handleTimeout() {
        console.error('WebSocket2 连接超时');
        // 关闭连接
        if (socket) {
          socket.close();
        }
      }
    
      // 定义重新连接函数
      function reconnect() {
        reconnectCount++;
        console.log(`尝试重新连接 WebSocket2,第 ${reconnectCount} 次...`);
        clearTimeout(timeoutTimer); // 清除超时计时器
    
        if (reconnectCount <= maxReconnectCount) {
          setTimeout(connect2, reconnectDelay); // 重新连接
          // 更新重连延迟时间,采用指数退避算法
          reconnectDelay = Math.min(2 * reconnectDelay, maxReconnectDelay);
        } else {
          console.error(`WebSocket2 重连失败,已达到最大重连次数 (${maxReconnectCount} 次)`);
        }
      }
    
      // 创建 WebSocket 连接
      socket = new WebSocket(URL);
    
      // 当连接打开时执行的操作
      socket.onopen = function (event) {
        console.log('WebSocket2 连接已建立');
        data[1].message = input2.value;
        data[1].time = new Date().toLocaleString();
        data[1].toUser = 'xhc';
        // 发送消息到服务器
        if (socket.readyState === WebSocket.OPEN) {
          // 如果连接处于 OPEN 状态,则通过连接对象发送消息
          socket.send(JSON.stringify(data[1]));
        } else {
          console.log('WebSocket2 连接未建立/连接建立失败/连接关闭');
        }
    
        input2.value = '';
    
        // 设置定时器,每隔30秒执行一次指定的回调函数
        timer = setInterval(() => {
          // 检查 WebSocket 连接的当前状态是否为 OPEN
          if (socket.readyState === WebSocket.OPEN) {
            // 如果连接处于 OPEN 状态,则通过连接对象发送心跳消息 'keep-alive'
            socket.send(JSON.parse('keep-alive'));
          }
        }, 30000); // 每 30 秒发送一次心跳
    
        // 设置超时计时器
        timeoutTimer = setTimeout(handleTimeout, reconnectDelay);
      };
    
      // 当接收到消息时执行的操作
      socket.onmessage = function (event) {
        mess2.innerHTML = `${mess2.innerHTML} <br>${event.data}`;
        console.log('来自服务器的消息2:', event.data);
    
        // 清除超时计时器
        clearTimeout(timeoutTimer);
      };
    
      // 当连接关闭时执行的操作
      socket.onclose = function (event) {
        mess2.innerHTML = 'WebSocket2 连接已关闭';
        console.log('WebSocket2 连接已关闭');
        // 清除定时器
        if (timer) {
          clearInterval(timer);
        }
       
      };
    
      // 当发生错误时执行的操作
      socket.onerror = function (error) {
        console.error('WebSocket2 连接发生错误:', error);
        // 清除超时计时器
        clearTimeout(timeoutTimer);
        clearInterval(timer);
      };
    };
    send2.addEventListener('click', connect2)
  </script>
</body>

</html>
相关推荐
M_emory_17 分钟前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Ciito21 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
成都被卷死的程序员1 小时前
响应式网页设计--html
前端·html
mon_star°1 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
Zrf21913184551 小时前
前端笔试中oj算法题的解法模版
前端·readline·oj算法
文军的烹饪实验室2 小时前
ValueError: Circular reference detected
开发语言·前端·javascript
Martin -Tang3 小时前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发3 小时前
解锁微前端的优秀库
前端
王解4 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁4 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis