📌 面试答不上的问题 - 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 服务器处理。


相关推荐
每次的天空3 分钟前
Android面试总结之GC算法篇
android·算法·面试
小黑随笔8 分钟前
【Golang玩转本地大模型实战(二):基于Golang + Web实现AI对话页面】
前端·人工智能·golang
蒟蒻小袁1 小时前
力扣面试150题--旋转链表
leetcode·链表·面试
肠胃炎1 小时前
CSS 内容超出显示省略号
前端·css
哟哟耶耶2 小时前
react-10样式模块化(./index.module.css, <div className={welcome.title}>Welcome</div>)
前端·javascript·react.js
Lysun0012 小时前
tailwindcss如何改变antd子组件的样式
前端·javascript·tailwindcss·antdesign
kooboo china.2 小时前
Tailwind CSS 实战:基于 Kooboo 构建企业官网页面(三)
前端·javascript·css·编辑器·html
生产队队长2 小时前
CSS:选择器-基本选择器
前端·css
神秘代码行者2 小时前
HTML Picture标签详细教程
前端·html
爱编程的鱼3 小时前
如何用CSS实现HTML元素的旋转效果
前端·css