当前位置: 首页 > news >正文

WebSocket与Socket.IO实现简易客服聊天系统全解析

WebSocket结合Socket.IO实现简易客服聊天系统全解析

一、技术选型对比

技术优点缺点适用场景
原生WebSocket浏览器原生支持,性能好API较底层,需手动处理断线重连等逻辑简单实时应用
Socket.IO自动重连,房间管理,兼容性好体积较大,有一定学习成本复杂实时应用,生产环境

为什么选择Socket.IO?

  • 内置断线自动重连
  • 支持房间/命名空间管理
  • 兼容老旧浏览器(自动降级)
  • 丰富的API和社区支持

二、核心实现流程

2.1 系统架构图

前端浏览器↑↓ HTTP/WebSocket
Node.js服务器(Socket.IO)↑↓ 数据库(可选)
消息持久化存储

2.2 前后端交互时序图

前端 后端 所有前端 指定前端 建立WebSocket连接 发送login事件(用户名) 返回login_success 广播user_list 发送private_message 定向转发消息 断开连接 更新user_list 前端 后端 所有前端 指定前端

三、前端代码详解

3.1 核心功能实现

功能代码片段说明
建立连接socket = io('http://localhost:3000')连接到指定服务器的Socket.IO服务
用户登录socket.emit('login', username)发送登录事件,携带用户名
接收消息socket.on('message', (data) => {...})监听服务器推送的消息
发送私聊socket.emit('private_message', {to, message})发送私聊消息到指定用户
用户列表更新socket.on('user_list', (users) => {...})实时更新在线用户列表

3.2 页面结构分析

<div class="container"><!-- 登录界面 --><div id="login"><input type="text" id="username"><button onclick="login()">登录</button></div><!-- 聊天界面 --><div id="chat" style="display: none;"><h3>当前用户:<span id="currentUser"></span></h3><div><h4>在线用户</h4><ul id="userList"></ul></div><div id="messages"></div><div><select id="receiver"><option value="">选择接收人</option></select><input type="text" id="messageInput"><button onclick="sendMessage()">发送</button></div></div>
</div>

3.3 js功能实现

先声明全局变量:
// 2.1 全局变量声明let socket;       // 用于存储Socket.IO连接实例let currentUser;  // 存储当前登录的用户名
用户登录,即进入房间:
// 2.2 登录功能函数function login() {// 获取用户名输入框的值并去除前后空格const username = document.getElementById('username').value.trim();// 如果用户名为空,直接返回if (!username) return;// 2.3 建立Socket.IO连接(连接到本地3000端口)socket = io('http://localhost:3000');// 存储当前用户名currentUser = username;// 2.4 Socket.IO事件监听// 连接成功事件socket.on('connect', () => {// 向服务器发送登录事件,传递用户名socket.emit('login', username);});// 登录成功事件socket.on('login_success', () => {// 隐藏登录界面,显示聊天界面document.getElementById('login').style.display = 'none';document.getElementById('chat').style.display = 'block';// 显示当前用户名document.getElementById('currentUser').textContent = username;});// 登录错误事件socket.on('login_error', (err) => {// 显示错误信息alert(err);});// 用户列表更新事件socket.on('user_list', (users) => {// 获取用户列表和接收者选择框的DOM元素const list = document.getElementById('userList');const receiver = document.getElementById('receiver');// 清空用户列表和接收者选择框list.innerHTML = '';receiver.innerHTML = '<option value="">选择接收人</option>';// 2.5 遍历用户列表,更新界面users.forEach(user => {// 不显示当前用户自己if (user !== currentUser) {// 添加到在线用户列表const li = document.createElement('li');li.textContent = user;list.appendChild(li);// 添加到接收者选择框const option = document.createElement('option');option.value = user;option.textContent = user;receiver.appendChild(option);}});});// 收到消息事件socket.on('message', (data) => {// 获取消息显示区域的DOM元素const messagesDiv = document.getElementById('messages');// 创建消息元素const msgEl = document.createElement('div');// 设置消息样式类,如果是自己发送的消息添加self类msgEl.className = `message ${data.self ? 'self' : ''}`;// 设置消息内容格式:[发送者 → 接收者]: 消息内容msgEl.textContent = `[${data.from}${data.to}]: ${data.message}`;// 将消息添加到消息显示区域messagesDiv.appendChild(msgEl);// 自动滚动到底部messagesDiv.scrollTop = messagesDiv.scrollHeight;});// 错误事件socket.on('error', (err) => {// 显示错误信息alert(err);});}
用户发送消息:
		// 2.6 发送消息函数function sendMessage() {// 获取接收者和消息内容const to = document.getElementById('receiver').value;const message = document.getElementById('messageInput').value.trim();// 如果接收者或消息为空,直接返回if (!to || !message) return;// 向服务器发送私聊消息socket.emit('private_message', {to,        // 接收者message    // 消息内容});// 清空消息输入框document.getElementById('messageInput').value = '';}

四、后端代码详解

为了实现1对1的聊天功能 to().emait('',data) ,我们需要知道你要发送的那个人的 socket.id ,这里就需要对每个登录的人与他的socket.id进行关联,因为 socket.id 前端不知道,所以后端需要创建一个new Map对象对用户的name进行映射,用户的name对应它的socket.id。在发消息的时候只需要知道要发的人的name即可进行get从map对象中获取对应的socket.id,从而实现一对一聊天

4.1 主要事件处理

事件类型处理逻辑业务作用
connection初始化socket连接,打印日志新客户端连接处理入口
login检查用户名冲突,更新users映射,广播user_list用户身份认证和在线状态管理
private_message查找接收者socket.id,分别向发送方和接收方发送消息实现私聊消息转发
disconnect从users映射中移除用户,广播更新后的user_list处理用户离线状态

4.2 环境配置

// 1.1 引入必要的Node.js模块
const http = require('http');       // HTTP服务器模块
const socketio = require('socket.io'); // Socket.IO模块// 1.2 创建基本的HTTP服务器
const server = http.createServer((req, res) => {// 对所有HTTP请求返回简单的HTML响应res.writeHead(200, { 'Content-Type': 'text/html' });res.end('<h1>Chat Server</h1>');
}).listen(3000, () => {// 服务器启动后打印日志console.log("Server running at http://localhost:3000");
});// 1.3 创建Socket.IO服务器并配置CORS
const io = socketio(server, {cors: {origin: "*", // 允许所有来源的跨域请求methods: ["GET", "POST"] // 允许的HTTP方法}
});

4.3 用户信息管理

// 2.1 创建用户映射表
// 使用Map存储用户名到socket.id的映射关系
// 结构: { 用户名 => socket.id }
const users = new Map();

4.4 具体功能实现

// 3.1 监听客户端连接事件
io.on('connection', (socket) => {// 当新客户端连接时打印日志console.log(`现在登录的用户socketid: ${socket.id}`);// 3.2 处理用户登录socket.on('login', (username) => {// 检查用户名是否已被占用if (users.has(username)) {// 发送登录错误消息socket.emit('login_error', '当前用户名已经被注册');return;}// 3.3 存储用户信息users.set(username, socket.id); // 将用户名和socket.id存入Mapsocket.username = username;     // 将用户名附加到socket对象上// 3.4 通知所有客户端更新用户列表// Array.from(users.keys()) 获取所有用户名数组io.emit('user_list', Array.from(users.keys()));// 通知当前客户端登录成功socket.emit('login_success');});// 3.5 处理私聊消息socket.on('private_message', ({ to, message }) => {// 参数解构: to是接收者用户名, message是消息内容// 3.6 查找接收者的socket.idconst targetSocketId = users.get(to);// 检查接收者是否存在if (!targetSocketId) {socket.emit('error', '用户未找到');console.log("用户未找到");return;}// 3.7 发送消息给发送者(自己)socket.emit('message', {from: socket.username, // 发送者to: to,               // 接收者message: message,     // 消息内容self: true            // 标记为自己发送的消息});// 3.8 发送消息给接收者// socket.to()方法指定接收者的socket.idsocket.to(targetSocketId).emit('message', {from: socket.username,to: to,message: message,self: false           // 标记为他人发送的消息});});// 3.9 处理断开连接socket.on('disconnect', () => {// 检查是否是已登录用户断开连接if (socket.username) {// 3.10 从用户映射中移除该用户users.delete(socket.username);// 3.11 通知所有客户端更新用户列表io.emit('user_list', Array.from(users.keys()));// 打印断开连接日志console.log(`用户断开连接: ${socket.username}`);}});
});

五、.on和.emit方法解惑

5.1 Socket.io 前端视角的 onemit 方法对比

从前端(客户端)的角度来看,socket.on()socket.emit() 的使用方式与后端类似,但角色和典型场景有所不同。以下是前端视角的对比表格:

特性socket.on('event', callback) (前端)socket.emit('event', data) (前端)
用途监听服务器发送的事件向服务器发送事件
方向接收服务器数据向服务器发送数据
回调函数有,处理服务器推送的数据无,只发送数据
作用范围监听特定服务器事件向服务器发送特定事件
是否等待响应被动接收服务器消息主动向服务器发起请求
典型使用场景接收通知、更新、广播消息等发送用户操作、请求数据等
**socket.on('event', callback) **
  • 用于监听服务器发送的事件
  • 当服务器触发对应事件时,回调函数会执行
  • 典型前端使用场景:
    • 接收实时通知
    • 获取数据更新
    • 监听广播消息
  • 示例:
// 监听服务器发送的消息
socket.on('newMessage', (message) => {console.log('收到新消息:', message);// 更新UI显示新消息
});// 监听服务器广播
socket.on('userJoined', (username) => {console.log(`${username} 加入了聊天室`);
});
socket.emit('event', data)
  • 用于向服务器发送事件和数据
  • 可以带参数发送给服务器
  • 典型前端使用场景:
    • 发送用户操作
    • 请求特定数据
    • 提交表单等
  • 示例:
// 发送聊天消息
socket.emit('sendMessage', {text: '你好!',room: 'general'
});// 请求加入房间
socket.emit('joinRoom', {roomId: '123',userId: 'user456'
});

5.2 Socket.io 的 onemit 方法对比

是的,socket.on()socket.emit() 在 Socket.io 中有不同的用途。下面是一个对比表格:

特性socket.on('event', callback)socket.emit('event', data)
用途注册/监听事件触发/发送事件
方向接收数据发送数据
回调函数有,处理接收的数据无,只发送数据
作用范围监听特定事件向特定目标发送事件
是否等待响应被动等待主动触发
典型使用场景服务器监听客户端请求客户端或服务器发送消息
socket.on('event', callback)
  • 用于注册事件监听器,等待接收特定事件
  • 当对应的事件被触发时,回调函数会被执行
  • 示例:
// 服务器端监听登录事件
socket.on('login', (credentials) => {console.log('收到登录请求:', credentials);// 验证逻辑...
});
socket.emit('event', data)
  • 用于触发事件并发送数据
  • 可以向特定客户端或所有客户端广播消息
  • 示例:
// 客户端发送登录请求
socket.emit('login', { username: 'user', password: 'pass' });// 服务器端广播消息给所有客户端
io.emit('message', '大家好!');
http://www.xdnf.cn/news/357139.html

相关文章:

  • Spring Web MVC快速入门
  • [css]纯css绘制三角形
  • MindSpore框架学习项目-ResNet药物分类-数据增强
  • HTML应用指南:利用POST请求获取全国德邦快递服务网点位置信息
  • C++中extern关键字详解:不同情况下的使用方式
  • Text Based Person Search 研究进展汇报:主要问题、数据集、未来方向
  • ATH12K驱动框架架构图
  • vue3 全局注册自定义指令,input聚焦失焦展示对应值
  • Java LocalDateTime类详解:高效处理日期时间
  • 面试题:Java集合框架高频面试题总结
  • 何时需要import css文件?怎么知道需要导入哪些css文件?为什么webpack不提示CSS导入?(导入css导入规则、css导入规范)
  • Nginx修改日志时间格式
  • WHAT - 用户访问产品个性化延迟问题和技术手段
  • 从投入产出、效率、上手难易度等角度综合对比 pytest 和 unittest 框架
  • 第三节:条件语句与循环:控制程序流程
  • 特殊配合力(SCA)作为全基因组关联分析(GWAS)的表型,其生物学意义和应用价值
  • MQTT:轻量级物联网通信协议详解
  • Vulnhub Lazysysadmin靶机攻击实战(一)
  • logback日志输出到项目运行目录
  • Git_idea界面进行分支合并到主分支详细操作
  • Elasticsearch内存管理与JVM优化:原理剖析与最佳实践
  • 两款Windows小工具,不收费还好用
  • 【网工第6版】第9章 网络管理
  • C语言实现小波变换去噪
  • 虚拟专用服务器(VPS)完全指南:从入门到选型
  • 红色大banner的wordpress免费模板
  • 【各种坐标系】
  • 学习笔记:黑马程序员JavaWeb开发教程(2025.3.31)
  • AI汽车时代的全面赋能者:德赛西威全栈能力再升级
  • k8s存储类型:emptyDir、hostPath、nfs、pvc及存储类storageclass的静态/动态创建pv