📌 面试答不上的问题 - WebSocket通信

📌 面试答不上的问题

2️⃣ WebSocket

WebSocket 是基于 TCP 全双工通信 的协议,允许客户端和服务器之间进行实时双向通信,适用于聊天、游戏、协同编辑等场景。

🔹 工作原理

  1. 客户端通过 ws://wss://(加密)协议向服务器发起握手请求。
  2. 服务器返回 101 状态码,升级 HTTP 连接为 WebSocket 连接。
  3. 双方可以双向发送消息,且不需要每次请求都建立新连接。

🔹 WebSocket 状态图示

状态 描述 事件
CONNECTING 正在建立连接 0 open
OPEN 连接成功,可以进行通信 1 message
CLOSING 连接正在关闭中 2 close
CLOSED 连接已关闭,无法再通信 3 close(连接关闭)

🔹 WebSocket 相关事件

WebSocket 提供了多个事件,用于处理不同的连接状态和数据通信场景。

  1. open 事件

    当 WebSocket 连接成功建立时触发。通常用于初始化连接后的一些操作,比如向服务器发送一条连接成功的消息。

    javascript 复制代码
    const socket = new WebSocket("wss://example.com");
    
    socket.onopen = (event) => {
      console.log("连接成功!");
      socket.send("Hello Server!");
    };
  2. message 事件

    当服务器发送消息到客户端时,message 事件会被触发。可以通过 event.data 获取服务器返回的数据。

    javascript 复制代码
    socket.onmessage = (event) => {
      console.log("收到服务器消息:", event.data);
    };
  3. error 事件

    如果 WebSocket 连接发生错误(如网络问题或服务器崩溃),error 事件会被触发。可以通过这个事件捕获连接错误并进行处理。

    javascript 复制代码
    socket.onerror = (event) => {
      console.error("WebSocket 发生错误:", event);
    };
  4. close 事件

    当 WebSocket 连接关闭时(无论是正常关闭还是发生错误),close 事件会被触发。可以通过该事件来清理一些资源或者执行断开连接后的操作。

    javascript 复制代码
    socket.onclose = (event) => {
      if (event.wasClean) {
        console.log(`连接正常关闭,代码 ${event.code},原因: ${event.reason}`);
      } else {
        console.error("连接异常关闭!");
      }
    };

📌 实现方式

🔹 服务器端(Node.js + ws 库)
javascript 复制代码
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });

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

  ws.send(JSON.stringify({ message: 'Welcome to WebSocket' }));

  ws.on('message', msg => {
    console.log('Received:', msg);
    ws.send(`Server received: ${msg}`);
  });

  ws.on('close', () => console.log('Client disconnected'));
});
🔹 客户端(浏览器)
javascript 复制代码
const ws = new WebSocket('ws://localhost:3000');

ws.onopen = () => {
  console.log('Connected to server');
  ws.send('Hello Server!');
};

ws.onmessage = event => {
  console.log('Message from server:', event.data);
};

ws.onclose = () => console.log('Connection closed');
🔹 Uniapp(多端支持)

📌 片段有点长 还请耐心阅读

API - uni.request封装
主要步骤:
  1. ConnectChatSocket(data) :这是对话的 WebSocket 请求方法,传入 data 参数。
  2. requestrequest 是一个封装了请求发送逻辑的工具函数,能够处理不同类型的请求(如 HTTP 请求和 WebSocket 请求)。通过 isWebSocket: true 来标识这次请求是一个 WebSocket 连接请求。
  3. wss://baidu.com/api/ws :这是 WebSocket 服务器的地址。注意,WebSocket 使用的是 wss:// 协议(即 WebSocket Secure),与 HTTP 的 https:// 协议类似,提供加密传输。
javascript 复制代码
// 流式请求-用于SSE
async function chunkedRequest(options = {}) {
  if (!options?.enableChunked) return;
  options.complete = () => ""; // 避免默认回调
  const requestTask = uni.request(options);
  if (typeof options?.TaskCallBack === "function") {
    requestTask.onChunkReceived((res) => options.TaskCallBack(res));
  }
  return requestTask;
}

// WebSocket 请求
async function socketRequest(options = {}) {
  if (!options?.isWebSocket) return;
  const requestTask = uni.connectSocket({
    url: `${options.url}?token=${store.state.user.tokenData}`,
    header: options?.header || {},
    method: options?.method || "GET",
    complete: () => "",
  });
  return requestTask;
}

// 统一请求封装
export default async function request(options = {}) {
  try {
    options = await interceptorsRequest(options); // 请求拦截
    if (options?.enableChunked) return chunkedRequest(options);
    if (options?.isWebSocket) return socketRequest(options);
    const [err, response] = await uni.request(options);
    return interceptorsResponse(err, response, options); // 响应拦截
  } catch (e) {
    return Promise.reject(e);
  }
}

// 对话 - WebSocket
function ConnectChatSocket(data) {
  return request({
      method: "GET",                          // 请求方法为 GET(WebSocket 不关心 HTTP 方法,但会使用 WebSocket 协议)
      url: "wss://baidu.com/api/ws",          // WebSocket 服务端地址,使用 `wss://` 协议
      isWebSocket: true,                      // 自定义字段,标记这是 WebSocket 请求
      data,                                   // 传递的数据,用于初始化连接时的消息或身份验证等
    });
},
Vuex数据管理中封装 chat.js
javascript 复制代码
// WebSocket 连接状态
export const SOCKET_STATUS = {
  CONNECTED: 1, // 连接成功
  PADDING_MSG: 2, // 发送消息成功 | 等待消息
  SEND_MSG_ERR: 3, // 发送消息失败
  CLOSED: 4, // 已断开
  ERRORED: 5, // 错误
};
创建 WebSocket 连接 (createWebSocket)
javascript 复制代码
async createWebSocket({ state, commit }, data) {
  const { isRefresh, MsgCallBack } = data || {};
  if (!isRefresh && state?.contentSocket) return state.contentSocket;
  const [, connect] = await apiFetch.Chat.ConnectChatSocket();
  if (!connect || typeof connect?.onOpen !== "function") {
    // 连接失败
    commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.ERRORED);
    return null;
  }
  await new Promise((resolve) => {
    connect.onOpen(() => {
      commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.CONNECTED);
      resolve(true);
    });
    connect.onMessage((res) => {
      if (typeof MsgCallBack === "function") {
        MsgCallBack(res);
      }
    });
    connect.onClose((res) => {
      console.error("WebSocket 已断开", res);
      commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.CLOSED);
      resolve(false);
    });
    connect.onError(() => {
      console.error("WebSocket 发生错误");
      Toast("连接服务器错误!");
      commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.ERRORED);
      resolve(false);
    });
  });
  commit("SET_CONTENT_SOCKET", connect);
  return connect;
},
发送消息 (sendSocket)
javascript 复制代码
async sendSocket({ state, commit, dispatch }, data) {
  if (
    !state.contentSocket ||
    typeof state.contentSocket?.send !== "function" ||
    state.contentSocketStatus !== SOCKET_STATUS.CONNECTED
  )
    return Promise.resolve(false);

  const isSend = await new Promise((resolve) => {
    const sendData = {
      id: data?.id || null,
      msg: data?.msg || "",
      type: data?.type || state.curChatModel || CHAT_MODEL.DEEPSEEK,
    };
    commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.PADDING_MSG);
    state.contentSocket.send({
      data: JSON.stringify(sendData),
      success: () => {
        if (!data?.id) {
          setTimeout(() => {
            dispatch("GetUserSessionsList", {
              isRefresh: true,
              notRefreshLog: true,
            });
          }, 2000);
        }
        resolve(true);
      },
      fail: (res) => {
        console.error("发送消息失败", res);
        commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.SEND_MSG_ERR);
        resolve(false);
      },
      complete: () => {
        dispatch("AddSessionsLog", { type: REPLY_TYPE.REPLY });
      },
    });
  });
  return isSend;
},
关闭 WebSocket (closeSocket)
javascript 复制代码
closeSocket({ state, commit }, data) {
  if (!state.contentSocket || typeof state.contentSocket?.close !== "function") return;
  console.error("关闭会话连接");
  state.contentSocket.close({ ...(data || {}), code: data?.code || 1000 });
  commit("SET_CONTENT_SOCKET", null);
  commit("SET_CONTENT_SOCKET_STATUS", SOCKET_STATUS.CLOSED);
},

📌 特点

全双工通信 ,支持客户端和服务器双向传输数据

✅ 适用于高频交互 (如 IM 聊天、在线游戏)。

✅ 支持二进制数据 (如文件、视频流)。

❌ 需要单独的 WebSocket 服务器,不能直接通过 HTTP 服务器处理。


相关推荐
paopaokaka_luck38 分钟前
基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
前端·javascript·vue.js·spring boot·后端·小程序·uni-app
患得患失9491 小时前
【前端】【vscode】【.vscode/settings.json】为单个项目配置自动格式化和开发环境
前端·vscode·json
飛_1 小时前
解决VSCode无法加载Json架构问题
java·服务器·前端
YGY Webgis糕手之路4 小时前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
90后的晨仔4 小时前
🚨XSS 攻击全解:什么是跨站脚本攻击?前端如何防御?
前端·vue.js
Ares-Wang4 小时前
JavaScript》》JS》 Var、Let、Const 大总结
开发语言·前端·javascript
90后的晨仔4 小时前
Vue 模板语法完全指南:从插值表达式到动态指令,彻底搞懂 Vue 模板语言
前端·vue.js
德育处主任5 小时前
p5.js 正方形square的基础用法
前端·数据可视化·canvas
烛阴5 小时前
Mix - Bilinear Interpolation
前端·webgl
90后的晨仔5 小时前
Vue 3 应用实例详解:从 createApp 到 mount,你真正掌握了吗?
前端·vue.js