解決用戶同時登錄輪詢獲取用戶信息錯亂,使用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 服務器,并監聽登錄狀態的消息。
    • 在收到消息后,更新用戶信息和登錄狀態。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/74322.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/74322.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/74322.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

發票查驗/發票驗真如何用Java實現接口調用

一、什么是發票查驗?發票驗真接口? 輸入發票基本信息發票代碼、發票號碼、開票日期、校驗碼后6位、不含稅金額、含稅金額,核驗發票真偽。 該接口也適用于機動車、二手車銷售發票、航空運輸電子客票、鐵路電子客票等。 二、如何用Java實現接口…

html5-qrcode前端打開攝像頭掃描二維碼功能

實現的效果如圖所示,全屏打開并且掃描到二維碼后彈窗提醒,主要就是使用html5-qrcode這個依賴庫,html5-qrcode開源地址:GitHub - mebjas/html5-qrcode: A cross platform HTML5 QR code reader. See end to end implementation at:…

cpp-友元

理解 C 中的友元(Friend) 在 C 語言中,封裝(Encapsulation) 是面向對象編程的重要特性之一。它允許類將數據隱藏在私有(private)或受保護(protected)成員中,…

JavaWeb基礎-HTTP協議、請求協議、響應協議

一. HTTP協議 1. HTTP協議:Hyper Text Transfer Protocol,超文本傳輸協議,規定了瀏覽器和服務器之間數據傳輸的規則 2. HTTP協議特點: ① 基于TCP協議:面向鏈接,安全 ② 基于請求-響應模型的:一…

search_fields與filterset_fields的使用

在Django中,search_fields 和 filterset_fields 可以在視圖類中使用,尤其是在 Django REST Framework (DRF) 中。它們分別用于實現搜索和過濾功能。以下是它們在視圖類中的具體使用方法。 1. search_fields 在視圖類中的使用 search_fields 是 DRF 中 S…

數據建模流程: 概念模型>>邏輯模型>>物理模型

數據建模流程 概念模型 概念模型是一種高層次的數據模型,用于描述系統中的關鍵業務概念及其之間的關系。它主要關注業務需求和數據需求,而不涉及具體的技術實現細節。概念模型通常用于在項目初期幫助業務人員和技術人員達成共識,確保對業務需…

在 Ubuntu 中用 Docker 安裝 RAGFlow

一、安裝 1.前提條件 CPU > 4 核 RAM > 16 GB Disk > 50 GB Docker > 24.0.0 & Docker Compose > v2.26.1 安裝docker:在Ubuntu中安裝Docker并配置國內鏡像 2.設置 vm.max_map_count #設置 vm.max_map_count 不小于 262144# 查看 sysctl vm.…

Java隨機生成n位驗證碼

Java學習筆記 今天寫一個隨機生成n位的驗證碼,包含字母大小寫和數字,直接見代碼。 package com.itheima.hello;// 生成一個隨機位數的驗證碼 public class ScannerDemo1 {public static void main(String[] args){System.out.println(getCode(4));Syst…

go復習目錄

全部都是博主的學習筆記,放著鏈接用的,自己收藏,包含基礎內容、go三方包、vue、數據結構、web框架、設計模式、docker、go連接kafka、redis、grpc、中間件 文章目錄 基礎內容go三方包vue數據結構web框架設計模式dockergo連接kafkaredisgrpc中…

23種設計模式-創建型模式-抽象工廠

文章目錄 簡介場景問題1. 風格一致性失控2. 對象創建硬編碼3. 產品族管理失效 解決總結 簡介 抽象工廠是一種創建型設計模式,可以生成相關對象系列,而無需指定它們的具體類。 場景 假設你正在寫一個家具店模擬器。 你的代碼這些類組成: 相…

案例:網絡命名空間模擬隔離主機場景

場景描述 假設我們需要在同一臺物理機上模擬兩臺獨立的主機(Host A 和 Host B),它們分別位于不同的網絡命名空間中,并通過虛擬以太網對(veth pair)進行通信。目標是展示網絡命名空間的隔離性和跨命名空間的…

新聞發布時間抽取(二)

1. 再論抽取方法 在前一期實驗中,對gne組件進行分析和完善,對三種時間抽取的方法進行了實驗對比。 在對抽取結果進行個例分析的過程中,我發現此前實驗存在幾個問題: 抽取的1000篇新聞存在一定的重復,經過ID去重大約減…

算法基礎——棧

一、棧的概念 棧是?種只允許在?端進?數據插?和刪除操作的線性表。 進?數據插?或刪除的?端稱為棧頂,另?端稱為棧底。不含元素的棧稱為空棧。進棧就是往棧中放?元素,出棧就是將元素彈出棧頂。 二、棧的模擬實現 1. 創建 本質還是線性表&#…

Android11至15系統定制篇

Android 11至15系統定制核心要點解析 一、Android 11關鍵定制特性 ?分區存儲強制化? 公共目錄(如Downloads、Pictures)與應用專屬目錄分離,應用更新后無法通過requestLegacyExternalStorage繞過限制?1。需申請MANAGE_EXTERNAL_STORAGE權限…

macOS 使用 enca 識別 文件編碼類型(比 file 命令準確)

文章目錄 macOS 上安裝 enca基本使用起因 - iconv關于 enca安裝 Encaenca & enconv 其它用法 macOS 上安裝 enca brew install enca基本使用 enca filepath.txt示例 $ enca 動態規劃算法.txt [0] Simplified Chinese National Standard; GB2312CRLF line terminat…

線段樹與掃描線 —— 詳解算法思想及其C++實現

目錄 一、線段樹(Segment Tree) 基本概念 結構 操作 示例代碼 二、掃描線(Sweep Line) 基本概念 應用場景 示例代碼(矩形面積并集) 三、總結 一、線段樹(Segment Tree) 基本…

匯編代碼中嵌入回調函數的優化說明

一、概述 在 PowerPC 的匯編代碼中,我們需要實現調用 C 函數(例如回調函數),并傳遞參數。本文將詳細介紹如何通過一系列步驟完成這一目標,包括代碼示例和詳細的注釋。 二、調用 C 函數的基本步驟及代碼 1. 保存工作寄…

Uni-App 雙欄聯動滾動組件開發詳解 (電梯導航)

本文基于提供的代碼實現一個左右聯動的滾動組件&#xff0c;以下是詳細的代碼解析與實現原理說明&#xff1a; <!--雙欄聯動滾動組件 - 技術解析功能特性&#xff1a;1. 左側導航欄與右側內容區雙向聯動2. 自適應容器高度3. 平滑滾動定位4. 動態內容位置計算 --> <te…

軟考復習-傳輸介質與編碼

傳輸介質 雙絞線 傳輸距離100一200m&#xff0c;即網線&#xff0c;有多種分類 UTP非屏蔽雙絞線 STP屏蔽雙絞線 線序標準有兩種為&#xff1a; T568A標準&#xff1a;綠白、綠、橙白、藍、藍白、橙、棕白、棕 T568B標準&#xff1a;橙白、橙、綠白、藍、藍白、綠、棕白、…

論文閱讀筆記:Denoising Diffusion Probabilistic Models (3)

論文閱讀筆記&#xff1a;Denoising Diffusion Probabilistic Models (1) 論文閱讀筆記&#xff1a;Denoising Diffusion Probabilistic Models (2) 論文閱讀筆記&#xff1a;Denoising Diffusion Probabilistic Models (3) 4、損失函數逐項分析 可以看出 L L L總共分為了3項…