前端面试练习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>
相关推荐
前端老宋Running1 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔1 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户4445543654263 分钟前
Android的自定义View
前端
WILLF4 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶21 分钟前
Axios使用教程(一)
前端
小章鱼学前端26 分钟前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah27 分钟前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝29 分钟前
手搓一个简简单单进度条
前端
倚栏听风雨1 小时前
详解 TypeScript 中,async 和 await
前端
小皮虾1 小时前
告别服务器!小程序纯前端“图片转 PDF”工具,隐私安全又高效
前端·javascript·微信小程序