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

解决用户同时登录轮询获取用户信息错乱,使用WebSocket和Server-Sent Events (SSE)

为什么更推荐WebSocket

Server-Sent Events (SSE) 是一种服务器向客户端推送数据的单向通信协议,适合某些场景,在解决用户同时登录和实时获取用户信息的问题上,WebSocket 是更好的选择


1. SSE 的局限性

单向通信
  • SSE 是单向的,只能从服务器向客户端推送数据,客户端无法通过 SSE 向服务器发送数据。
  • 如果需要双向通信(如用户登录后需要发送确认消息),SSE 无法满足需求。
连接限制
  • 浏览器对 SSE 的连接数有限制(通常每个域名最多 6 个并发连接)。
  • 如果用户同时打开多个页面,可能会占用大量连接,导致新的连接无法建立。
协议支持
  • SSE 基于 HTTP 协议不支持二进制数据传输只能传输文本数据
  • 如果需要传输二进制数据(如图片、文件),SSE 无法实现。
兼容性
  • 虽然现代浏览器都支持 SSE,但在某些特殊环境(如旧版浏览器或移动端)可能存在兼容性问题

2. WebSocket 的优势

双向通信
  • WebSocket 是全双工的,支持服务器和客户端之间的双向通信。
  • 适合需要客户端和服务器交互的场景(如用户登录后需要发送确认消息)。
高性能
  • WebSocket 的连接是持久的,只有在有数据更新时才会传输数据,减少不必要的请求。
  • 适合高频更新的场景(如实时通知、聊天应用)。
支持二进制数据
  • WebSocket 支持二进制数据传输,适合传输图片、文件等数据。
多用户并发
  • WebSocket 可以为每个用户维护独立的连接,避免多个用户之间的数据冲突。
  • 适合多用户并发的场景。

3. 场景对比

特性WebSocketSSE
通信方式双向通信单向通信(服务器 → 客户端)
性能高性能,适合高频更新适合低频更新
数据传输支持文本和二进制数据仅支持文本数据
连接限制无连接限制每个域名最多 6 个并发连接
兼容性现代浏览器均支持部分旧版浏览器不支持
适用场景实时通知、聊天应用、多用户并发低频通知、状态更新

4. 为什么选择 WebSocket?

用户同时登录时需要实时获取用户信息,且可能需要双向通信(如用户登录后需要发送确认消息)。因此,WebSocket 是更好的选择,因为它:

  • 支持双向通信,满足复杂交互需求。
  • 性能高,适合高频更新。
  • 支持多用户并发,避免数据冲突。

5. SSE 的适用场景

SSE 适合以下场景:

  • 低频通知:如新闻更新、股票价格变动。
  • 状态更新:如任务进度、系统状态。
  • 单向通信:只需要服务器向客户端推送数据。

如果你的场景是单向的、低频的,且不需要客户端向服务器发送数据,SSE 是一个简单的选择。


6. 总结

  • 推荐使用 WebSocket,因为它支持双向通信性能高适合多用户并发
  • SSE 适合低频、单向的场景,但在用户同时登录和实时获取用户信息的问题上,WebSocket 是更好的选择。

实现步骤

解决用户同时登录时轮询获取用户信息出错的问题,推荐使用 WebSocket。以下是详细的原因和实现方法:


1. 后端实现 WebSocket 服务器

安装依赖
npm install ws
代码实现
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 }); // WebSocket 服务器端口// 存储所有连接的客户端
const clients = new Map();wss.on('connection', (ws, req) => {console.log('客户端已连接');// 从请求中获取用户标识(如 openid)const openid = new URL(req.url, 'http://localhost').searchParams.get('openid');if (openid) {clients.set(openid, ws); // 将用户标识和 WebSocket 连接关联}// 监听客户端消息ws.on('message', (message) => {console.log('收到客户端消息:', message);});// 监听连接关闭ws.on('close', () => {console.log('客户端已断开连接');if (openid) {clients.delete(openid); // 移除用户标识}});
});// 推送登录状态
function pushLoginStatus(openid, userInfo) {const ws = clients.get(openid);if (ws) {ws.send(JSON.stringify({type: 'login_success',data: {openid,userInfo}}));}
}module.exports = { pushLoginStatus };

2. 在用户登录成功后推送消息

在用户登录成功的逻辑中,调用 pushLoginStatus 推送消息。

const { pushLoginStatus } = require('./websocket'); // 引入 WebSocket 模块app.get('/auth/callback', async (req, res) => {const { code } = req.query;if (!code) {return res.status(400).send("Code 参数获取失败");}try {// 1. 用 code 换取 access_token 和 openidconst { data: tokenData } = await axios.get(`https://api.weixin.qq.com/sns/oauth2/access_token`, {params: { appid, secret: appsecret, code, grant_type: "authorization_code" }});if (!tokenData.access_token || !tokenData.openid) return res.status(400).send("获取 access_token 失败");if (tokenData.scope !== 'snsapi_userinfo') return res.status(400).send("用户未授权");const { access_token, openid } = tokenData;// 2. 获取用户信息const { data: userInfo } = await axios.get(`https://api.weixin.qq.com/sns/userinfo`, {params: { access_token, openid, lang: "zh_CN" }});const { nickname, sex, province, city, headimgurl } = userInfo;// 3. 查询数据库,判断用户是否存在db.query(`SELECT * FROM users WHERE openid = ?`, [openid], (err, results) => {if (err) return res.status(500).send("数据库查询失败");if (results.length > 0) {// 老用户,更新 is_new 为 0db.query(`UPDATE users SET is_new = 0 WHERE openid = ?`,[openid],(updateErr, updateResults) => {if (updateErr) {console.error("更新 is_new 失败:", updateErr);return res.status(500).send("更新 is_new 失败");}// 推送登录状态pushLoginStatus(openid, results[0]);return res.status(200).send({status: 'success',message: "用户已存在",is_new: 0,data: results[0]});});} else {// 新用户,存入数据库db.query(`INSERT INTO users (openid, nickname, sex, province, city, headimgurl, is_new)VALUES (?, ?, ?, ?, ?, ?, 1)`,[openid, nickname, sex, province, city, headimgurl],(insertErr, insertResults) => {if (insertErr) {console.error("数据库操作失败:", insertErr);return res.status(500).send("数据库操作失败");}// 推送登录状态pushLoginStatus(openid, { openid, nickname, sex, province, city, headimgurl });return res.status(200).send({status: 'success',message: "新用户信息保存成功",is_new: 1,data: { openid, nickname, sex, province, city, headimgurl }});});}});} catch (err) {console.error("服务器错误:", err);res.status(500).send({status: 'error',message: "服务器错误",error: err.message || err.toString()});}
});

3. 前端实现 WebSocket 客户端

代码实现
export default {data() {return {isLoggedIn: false,userInfo: {},ws: null};},created() {this.connectWebSocket();},methods: {// 连接 WebSocketconnectWebSocket() {const openid = localStorage.getItem('userOpenID');if (!openid) return;this.ws = new WebSocket(`ws://localhost:8080?openid=${openid}`);this.ws.onopen = () => {console.log('已连接到 WebSocket 服务器');};this.ws.onmessage = (event) => {const data = JSON.parse(event.data);if (data.type === 'login_success') {localStorage.setItem('userOpenID', data.data.openid);this.isLoggedIn = true;this.userInfo = data.data.userInfo;this.$store.dispatch('saveUserInfo', this.userInfo);}};this.ws.onclose = () => {console.log('WebSocket 连接已关闭');};}},beforeDestroy() {// 组件销毁时关闭 WebSocket 连接if (this.ws) {this.ws.close();}}
};

4. 总结

  • 后端

    • 使用 WebSocket 服务器推送用户登录状态。
    • 在用户登录成功后,通过 pushLoginStatus 推送消息。
  • 前端

    • 连接 WebSocket 服务器,并监听登录状态的消息。
    • 在收到消息后,更新用户信息和登录状态。

相关文章:

解决用户同时登录轮询获取用户信息错乱,使用WebSocket和Server-Sent Events (SSE)

为什么更推荐WebSocket Server-Sent Events (SSE) 是一种服务器向客户端推送数据的单向通信协议,适合某些场景,在解决用户同时登录和实时获取用户信息的问题上,WebSocket 是更好的选择。 1. SSE 的局限性 单向通信 SSE 是单向的&#xff0…...

LLM之RAG理论(十四)| RAG 最佳实践

RAG 的过程很复杂,包含许多组成部分。我们如何确定现有的 RAG 方法及其最佳组合,以确定最佳 RAG 实践? 论文 《Searching for Best Practices in Retrieval-Augmented Generation》给出了回答。 本文将从以下三方面进行介绍: 首先…...

[RoarCTF 2019]Easy Calc-3.23BUUCTF练习day5(2)

[RoarCTF 2019]Easy Calc-3.23BUUCTF练习day5(2) 解题过程 查看源码 发现calc.php页面,访问一下 分析代码 首先获取$_GET[num]的值并赋给变量$str。然后定义了一个黑名单数组$blacklist,包含了一系列被禁止的字符或转义字符,如空格、制表…...

hadoop集群配置-ssh无密登录

1.ssh-keygen -t rsa 2.ssh-copy-id hadoop1 3.ssh roothadoop1 退出 exit...

【C++教程】break语句

在 C 中,break 是一个控制流语句,用于立即终止当前所在的循环或 switch 语句的执行,并跳出其作用域。以下是 break 的详细用法及场景: 1. 在循环中使用 break break 会直接终止当前所在的循环(for、while、do-while&a…...

MinGW与使用VScode写C语言适配

压缩包 通过网盘分享的文件:MinGW.zip 链接: https://pan.baidu.com/s/1QB-Zkuk2lCIZuVSHc-5T6A 提取码: 2c2q 需要下载的插件 1.翻译 找到VScode页面,从上数第4个,点击扩展(以下通此) 搜索---Chinese--点击---安装--o…...

openharmony中hilog实证记录说明(3.1和5.0版本)

每次用这个工具hilog都有一些小用法记不清,需要花一些时间去查去分析使用方法,为了给丰富多彩的生活留出更多的时间,所以汇总整理共享来了,它来了它来了~~~~~~~~~ 开始是想通过3.1来汇总的,但实际测试发现openharmony…...

算法刷题整理合集(七)·【算法赛】

本篇博客旨在记录自已的算法刷题练习成长,里面注有详细的代码注释以及和个人的思路想法,希望可以给同道之人些许帮助。本人也是算法小白,水平有限,如果文章中有什么错误或遗漏之处,望各位可以在评论区指正出来&#xf…...

Android Studio控制台中文乱码解决方案

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 👉点击跳转到教程 前言: 在项目调试过程中,用华为手机调试控制台没任何问题&#x…...

BUAA XCPC 2025 Spring Training 2

C \color{green}{\texttt{C}} C [Problem Discription] \color{blue}{\texttt{[Problem Discription]}} [Problem Discription] 给定一棵以 1 1 1 为根的树,记 a i a_{i} ai​ 表示节点 i i i 的权值, lca( i , j ) \text{lca(}i,j) lca(i,j) 表示节…...

Edge浏览器如何默认启动某个工作区 / 为工作区添加快捷方式

Edge浏览器的工作区确实非常好用,可以多端同步标签页。但是打开Edge时默认是没有在工作区的状态,这个状态下的标签页可能会丢失。所以我研究了一下,如何点击快捷方式时自动启动一个工作区,方法如下: 先找到WorkspaceCa…...

Cherry Studio搭建本地知识库,结合DeepSeek实现RAG

Cherry Studio搭建本地知识库,结合DeepSeek实现RAG CherryStudioCherryStudio 简介环境准备 模型配置本地知识创建1、新建知识库2、添加文件3、添加网址或者网站4、搜索知识库 结合DeepSeek实现RAG1、选择知识库2、进行提问 常见问题与解决方案 CherryStudio Cherr…...

【Android】VehiclePropertyAccess引起CarService崩溃

VehiclePropertyAccess引起CarService崩溃 VehiclePropertyAccess VehiclePropertyAccess属性,用于定义车辆属性的访问权限。权限包括 读:READ,只可以读取,不能写入。 VehiclePropertyAccess:READ写:WRITE&#xf…...

深度剖析:复制带随机指针的链表算法实现

在链表相关的算法中,复制一个带有随机指针的链表是一个经典且具有一定难度的问题。本文将深入分析一段用C语言实现的复制带随机指针链表的代码,通过模块化的方式详细解释每段代码的作用,帮助读者更好地理解这一复杂算法。 作者主页&#xf…...

Java 大视界 -- Java 大数据在智慧文旅旅游目的地营销与品牌传播中的应用(150)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...

SQLMesh SCD-2 时间维度实战:餐饮菜单价格演化追踪

场景背景:动态菜单价格管理 考虑某连锁餐厅的菜单管理系统,需要记录食品价格的历史变更轨迹。业务需求包括: 记录每次价格调整的时间点支持历史价格查询(如"2020年1月2日汉堡多少钱")维护当前有效价格清单…...

uniapp自身bug | uniapp+vue3打包后 index.html无法直接运行

前提: 已经修改了基础路径 打开打包文件,双击运行index.html报错,无法访问页面 uniappvue2项目是可以正常运行的 vue3修改publicPath: ./后,也是可以正常访问打包文件中的index.html 点进控制台提供的链接:https:/…...

数据分析面试--京东

1.考察日期函数的应用 select Order_date, count(distinct user_id) as uv from (select user_id, Order_date, row_number() over(partition by user_id order by Order_date) as new_tagfrom ord where date_diff(current_date(), Order_date)<30 ) t where new_tag1 gro…...

Centos7搭建Zabbix4.x监控HCL模拟网络设备:zabbix-server搭建及监控基础04

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 4.OID查看工具Getif安装及使用 找度娘下载Getif&#xff0c;该软件比较老&#xff0c;可以用来查看OID编码&#xff0c;我的宿主机是Win11,无法安装。所以只有到虚拟机win12去安装&am…...

爬虫:scrapy面试题大全(60个scrapy经典面试题和详解)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. 什么是Scrapy?2. Scrapy 框架的组件及其作用?3. Scrapy的工作流程是什么?(运行机制)4. 如何创建一个Scrapy项目?5. 如何定义一个Spider?6. 如何在Scrapy中提取数据?7. Scrapy中的Item是什么?8. Scrapy中的P…...

Ubuntu Debian 系统下挂载 Samba 共享目录的完整指南

文章目录 Ubuntu & Debian 系统下挂载 Samba 共享目录的完整指南前提条件挂载 Samba 共享临时挂载避免明文密码永久挂载 常见选项卸载故障排查 Ubuntu & Debian 系统下挂载 Samba 共享目录的完整指南 想把NAS中的内容通过Samba挂载到 OrangePi 5B&#xff0c;但是 Ora…...

蓝桥杯2023年第十四届省赛真题-异或和之差

题目来自DOTCPP&#xff1a; 思路&#xff1a; 什么是异或和&#xff1f; ①题目要求我们选择两个不相交的子段&#xff0c;我们可以枚举一个分界线i&#xff0c;子段1在 i 的左边&#xff0c; 子段2在 i 的右边&#xff0c;分别找到子段1和子段2的最大值、最小值。 ②怎么确…...

考研课程安排(自用)

文章目录 408数据结构&#xff08;王道&#xff09;计算机组成原理&#xff08;王道&#xff09;操作系统&#xff08;王道&#xff09;计算机网络&#xff08;湖科大版&#xff09; 数学一高等数学&#xff08;微积分&#xff09;线性代数和概率论 408 数据结构&#xff08;王…...

linux命令行工具进阶

文章目录 前言ssh免密登录&#xff0c;免密码登录&#xff0c;公私钥查看与修改IP地址临时修改永久修改 mount临时切换根文件系统永久切换根文件系统loop文件partedinitramfsuboot command line总结 前言 本文记录了一些不经常用到&#xff0c;但在某个时刻需要用到的一些指令…...

Linux系统管理实战:文件权限配置、用户组协作与日志处理全解析

1、创建/www目录&#xff0c;在/www目录下新建name和https目录&#xff0c;在name和https目录下分别创建一个index.html文件&#xff0c;name下面的index.html文件中包含当前主机的主机名&#xff0c;https目录下的index.html文件中包含当前主机的ip地址。 &#xff08;1&…...

[自动化] 【八爪鱼】使用八爪鱼实现CSDN文章自动阅读脚本

在CSDN上&#xff0c;文章的阅读量往往是衡量内容影响力的一个重要指标。为了测试自动化手段能否提高阅读数&#xff0c;我尝试使用网页自动化工具来模拟人工阅读某个ID的文章。 1. 网页自动化的常见方案 谈到网页自动化&#xff0c;Selenium 是一个最常见的选择。它可以通过…...

Go语言分布式锁实战:dlock助力构建高并发稳定系统

在构建分布式系统时&#xff0c;一个常见且棘手的问题便是资源竞争和数据一致性问题。分布式锁作为一种常用的解决方案&#xff0c;在多个进程或节点之间协调访问共享资源时显得尤为重要。今天&#xff0c;我们将介绍一款分布式锁库——dlock&#xff0c;并通过详细的使用示例带…...

如何提高G口服务器的安全性?

G口服务器可以支持千兆网络传输速度&#xff0c;能够为企业提供更快的数据处理能力和传输能力&#xff0c;随着网络流量的不断增长以及复杂计算任务的普及&#xff0c;企业对于网络带宽的要求也在相应提高&#xff0c;而G口服务器则可以降低网络的延迟度&#xff0c;大幅度提高…...

为没有CMake配置的第三方库添加CMake配置

1 编写CMakeLists.txt cmake_minimum_required(VERSION 3.15) #如果你第三方库和自己的库没有xxxConfig.cmake #请修改项目名称和命名空间&#xff08;一般不需要&#xff09; project("pthreads" LANGUAGES C CXX) set(KC_NAMESPACE "") #set(K…...

Linux驱动编程 - seq_open、single_open使用方法

目录 前言: 一、seq_xxx 1、seq_xxx 函数介绍 1.1 seq_open 1.2 seq_read 1.3 seq_lseek 1.4 seq_release 1.5 格式化输出函数 2、seq_open 实例 二、single_xxx 函数 1、single_xxx 函数介绍 1.1 single_open 1.2 single_start 1.3 single_next 1.4 single_stop…...