socket.io-client实现实前后端时通信功能

这里我使用的后端 基于node.js的koa框架 前端使用的是vite

{"name": "hou","version": "1.0.0","description": "","main": "app.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","start": "node app.js","dev": "nodemon app.js"},"author": "","license": "ISC","dependencies": {"@koa/cors": "^5.0.0","bcryptjs": "^2.4.3","jsonwebtoken": "^9.0.2","koa": "^2.15.3","koa-bodyparser": "^4.4.1","koa-jwt": "^4.0.4","koa-router": "^13.0.1","nodemon": "^3.1.7","ws": "^8.18.0"}
}
const Koa = require("koa");
const Router = require("koa-router");
const bodyParser = require("koa-bodyparser");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const koaJwt = require("koa-jwt");
const cors = require("@koa/cors");const app = new Koa();
const router = new Router();
const secret = "supersecretkey"; // 用于签发 JWT 的密钥app.use(cors());
app.use(bodyParser());// 模拟用户数据库
const users = [{ id: 1, username: "user1", password: bcrypt.hashSync("password", 10) },{ id: 2, username: "user2", password: bcrypt.hashSync("password", 10) },{ id: 3, username: "user3", password: bcrypt.hashSync("password", 10) },{ id: 4, username: "user4", password: bcrypt.hashSync("password", 10) },{ id: 5, username: "user5", password: bcrypt.hashSync("password", 10) },
];// 登录路由
router.post("/login", async (ctx) => {const { username, password } = ctx.request.body;const user = users.find((u) => u.username === username);if (!user) {ctx.status = 400;ctx.body = { message: "没有此用户" };return;}const validPassword = bcrypt.compareSync(password, user.password);if (!validPassword) {ctx.status = 400;ctx.body = { message: "密码错误" };return;}const token = jwt.sign({ id: user.id, username: user.username }, secret, {expiresIn: "1h",});ctx.body = { token };
});// 获取用户信息路由(登录后可用)
router.get("/me", koaJwt({ secret }), async (ctx) => {const user = users.find((u) => u.id === ctx.state.user.id);ctx.body = user;
});// 中间件:使用 JWT 验证
app.use(koaJwt({ secret }).unless({ path: [/^\/login/] }));// 搜索用户接口(登录后可用)
router.get("/search", async (ctx) => {const query = ctx.query.q;const result = users.filter((user) => user.username.includes(query));ctx.body = result;
});// 添加好友接口(登录后可用)
let friends = {}; // { userId: [friendId1, friendId2, ...] }
router.post("/add-friend", async (ctx) => {const { friendId } = ctx.request.body;const userId = ctx.state.user.id; // 从 JWT 中提取用户信息if (!friends[userId]) {friends[userId] = [];}if (!friends[userId].includes(friendId)) {friends[userId].push(friendId);}ctx.body = { message: "Friend added" };
});// WebSocket 服务同样需要身份验证
const WebSocket = require("ws");
const http = require("http");
const server = http.createServer(app.callback());
const wss = new WebSocket.Server({ server });wss.on("connection", (ws, req) => {const token = req.url.split("?token=")[1];if (!token) {ws.close();return;}try {const decoded = jwt.verify(token, secret);ws.userId = decoded.id;ws.send("Connected to chat");ws.on("message", (message) => {// 广播消息仅限好友之间const friendIds = friends[ws.userId] || [];wss.clients.forEach((client) => {if (client.readyState === WebSocket.OPEN &&friendIds.includes(client.userId)) {client.send(message);}});});} catch (error) {ws.close();}
});app.use(router.routes()).use(router.allowedMethods());server.listen(3000, () => {console.log("Server running on http://localhost:3000");
});

前端

{"name": "vite-project","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "vite build","preview": "vite preview"},"dependencies": {"socket.io-client": "^4.8.0","vue": "^3.4.37","vue-router": "^4.4.5"},"devDependencies": {"@vitejs/plugin-vue": "^5.1.2","vite": "^5.4.1"}
}

登录

<template><div><input v-model="username" placeholder="Username" /><input type="password" v-model="password" placeholder="Password" /><button @click="login">Login</button></div>
</template><script>
export default {data() {return {username: "",password: "",};},methods: {async login() {const response = await fetch("http://localhost:3000/login", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({username: this.username,password: this.password,}),});const data = await response.json();if (response.ok) {localStorage.setItem("token", data.token);this.$router.push("/"); // 登录成功后跳转到聊天页面} else {alert(data.message);}},},
};
</script>
<template><div><div><span>欢迎;{{ user.username }}</span></div><!-- 搜索和添加好友功能 --><div><input v-model="searchQuery" placeholder="Search for users" /><button @click="searchUsers">Search</button><div v-if="searchResults.length > 0"><h3>Search Results</h3><div v-for="user in searchResults" :key="user.id">{{ user.username }}<button @click="addFriend(user.id)">Add Friend</button></div></div></div><!-- 好友列表 --><div v-if="friends.length > 0"><h3>Friends List</h3><ul><liv-for="friend in friends":key="friend.id"@click="startChat(friend)">{{ friend.username }}</li></ul></div><!-- 聊天窗口 --><div v-if="currentChatUser"><h3>Chatting with {{ currentChatUser.username }}</h3><div class="chat-window"><divv-for="(msg, index) in messages":key="index":class="{ sent: msg.sentByUser, received: !msg.sentByUser }">{{ msg.text }} <span class="timestamp">{{ msg.timestamp }}</span></div></div><inputv-model="newMessage"@keyup.enter="sendMessage"placeholder="Type a message..."/><button @click="sendMessage">Send</button></div></div>
</template><script>
export default {data() {return {searchQuery: "", // 搜索框的输入searchResults: [], // 搜索结果friends: [], // 好友列表socket: null, // WebSocket 连接messages: [], // 聊天记录newMessage: "", // 输入的新消息currentChatUser: null, // 当前聊天的好友user: {}, // 当前用户信息};},created() {// 获取当前用户信息const token = localStorage.getItem("token");fetch("http://localhost:3000/me", {headers: { Authorization: `Bearer ${token}` },}).then((response) => response.json()).then((user) => {this.user = user;});},methods: {// 搜索用户async searchUsers() {const token = localStorage.getItem("token");const response = await fetch(`http://localhost:3000/search?q=${this.searchQuery}`,{headers: { Authorization: `Bearer ${token}` },});this.searchResults = await response.json();},// 添加好友async addFriend(friendId) {const token = localStorage.getItem("token");const response = await fetch("http://localhost:3000/add-friend", {method: "POST",headers: {Authorization: `Bearer ${token}`,"Content-Type": "application/json",},body: JSON.stringify({ friendId }),});if (response.ok) {alert("Friend added!");// 可选:可以在添加好友后,更新好友列表this.friends.push(this.searchResults.find((user) => user.id === friendId));}},// 开始与某个好友聊天startChat(friend) {this.currentChatUser = friend; // 设置当前聊天用户this.messages = []; // 清空当前消息// 连接 WebSocket(与服务器端的 WebSocket 实现保持一致)if (!this.socket) {const token = localStorage.getItem("token");this.socket = new WebSocket(`ws://localhost:3000?token=${token}`);// 监听 WebSocket 消息this.socket.onmessage = (event) => {if (event.data instanceof Blob) {// 如果是 Blob 类型的数据,将其转换为文本const reader = new FileReader();reader.onload = (event) => {const text = event.target.result;const message = {text: text,sentByUser: false,timestamp: new Date().toLocaleTimeString(), // 添加时间戳};this.messages.push(message); // 将消息添加到消息列表};reader.readAsText(event.data);} else {// 如果不是 Blob 数据,直接将消息显示const message = {text: event.data,sentByUser: false,timestamp: new Date().toLocaleTimeString(),};this.messages.push(message);}};}},// 发送消息sendMessage() {if (this.newMessage.trim() !== "") {const message = {text: this.newMessage,sentByUser: true,timestamp: new Date().toLocaleTimeString(), // 获取当前时间};this.messages.push(message); // 添加到消息列表this.socket.send(this.newMessage); // 发送消息this.newMessage = ""; // 清空输入框}},},
};
</script><style>
/* 样式调整 */
.chat-window {border: 1px solid #ccc;padding: 10px;height: 300px;overflow-y: scroll;margin-bottom: 10px;
}.sent {text-align: right;background-color: #d1f0d1; /* 发送的消息背景色 */
}.received {text-align: left;background-color: #f0f0f0; /* 接收的消息背景色 */
}.timestamp {font-size: 0.8em;color: #888; /* 时间戳颜色 */
}
</style>
import { createRouter, createWebHistory } from "vue-router";const routes = [{path: "/",name: "Home",component: import("../views/Home.vue"),},{path: "/login",name: "Login",component: import("../views/Login.vue"),},
];const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes,
});export default router;

在这里插入图片描述
主体功能初步实现,后期可以优化方向,设计数据库 将添加过的好友存储在数据表中,添加好友要先通过才能添加

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/434632.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

老古董Lisp实用主义入门教程(12):白日梦先生的白日梦

白日梦先生的白日梦 白日梦先生已经跟着大家一起学Lisp长达两个月零五天&#xff01; 001 粗鲁先生Lisp再出发002 懒惰先生的Lisp开发流程003 颠倒先生的数学表达式004 完美先生的完美Lisp005 好奇先生用Lisp来探索Lisp006 好奇先生在Lisp的花园里挖呀挖呀挖007 挑剔先生给出…

手游和应用出海资讯:三七新游首月收入突破700万元;领英尝试推出游戏功能以增加用户使用时长

NetMarvel帮助游戏和应用广告主洞察全球市场、获取行业信息&#xff0c;以下为9月第四周资讯&#xff1a; ● 《AFK Journey》收入突破 1.5 亿美元 ● 《黑神话&#xff1a;悟空》IGN年度游戏投票第一掉至第三 ● 三七发布新游首月收入突破700万元 ● 开罗游戏《哆啦A梦的铜锣烧…

Java SPI 原理、样例

在 Java 中&#xff0c;SPI&#xff08;Service Provider Interface&#xff09;全称为服务提供者接口&#xff0c;它是一种用于实现框架扩展和插件化的机制。 一、SPI 作用 允许在运行时动态地为接口查找服务实现&#xff0c;而不需要在代码中显式地指定具体的实现类。 这使得…

关系模型与关系代数——数据库原理 总结2

2.1 关系模型 关系数据结构 关系模型的数据结构是二维表&#xff0c;亦称为关系。关系数据库是表的集合&#xff0c;即关系的集合。表是一个实体集&#xff0c;一行就是一个实体&#xff0c;它由有关联的若干属性的值所构成。 关系模型的相关概念 列就是数据项 或 字段 或 属…

基于SpringCloud的微服务架构下安全开发运维准则

为什么要进行安全设计 微服务架构进行安全设计的原因主要包括以下几点&#xff1a; 提高数据保护&#xff1a;微服务架构中&#xff0c;服务间通信频繁&#xff0c;涉及到大量敏感数据的交换。安全设计可以确保数据在传输和存储过程中的安全性&#xff0c;防止数据泄露和篡改。…

Study--Oracle-09--部署Openfiler存储服务器

免费的存储服务器软件有FreeNAS 和 Openfiler。 其中Freenas的网站上只有i386及amd64的版本,也就是说Freenas不能支持64位版本的Intel CPU,而Openfiler则提供更全面的版本支持,在其网站上可以看到支持多网卡、多CPU,以及硬件Raid的支持,还有10Gb网卡的支持。 Openfiler能把…

【RocketMQ】RocketMQ发送不同类型消息

&#x1f3af; 导读&#xff1a;本文介绍了RocketMQ消息队列系统中的几种消息发送模式及其应用场景&#xff0c;包括同步消息、异步消息以及事务消息。同步消息确保了消息的安全性&#xff0c;但牺牲了一定的性能&#xff1b;异步消息提高了响应速度&#xff0c;适用于对响应时…

演示:基于WPF的DrawingVisual开发的频谱图和律动图

一、目的&#xff1a;基于WPF的DrawingVisual开发的频谱图和律动图 二、效果演示 波形图 极坐标 律动图极坐标图 律动图柱状图 Dock布局组合效果 三、环境 VS2022,Net7,Win10&#xff0c;NVIDIA RTX A2000 四、主要功能 支持设置起始频率&#xff0c;终止频率&#xff0c;中心…

【HTTP 和 HTTPS详解】3

HTTP 状态代码 HTTP 状态代码是服务器发送给客户端的三位数字&#xff0c;用于指示客户端请求的结果。它们分为五类&#xff1a;信息性&#xff08;100-199&#xff09;、成功&#xff08;200-299&#xff09;、重定向&#xff08;300-399&#xff09;、客户端错误&#xff08…

【移植】Combo解决方案之W800芯片移植案例

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 本方案基于 OpenHarmony LiteOS-M 内核&#xff0c;使用联盛德 W80…

[论文精读]Membership Inference Attacks Against Machine Learning Models

中文译名&#xff1a;针对机器学习模型的成员推理攻击 会议名称&#xff1a;2017 IEEE Symposium on Security and Privacy (SP) 发布链接&#xff1a;Membership Inference Attacks Against Machine Learning Models | IEEE Conference Publication | IEEE Xplore CODE:Git…

【计算机网络】Tcp报文的组成,Tcp是如何实现可靠传输的?

Tcp的报文组成 TCP&#xff08;传输控制协议&#xff09;是计算机网络中一种重要的传输协议&#xff0c;其报文组成包括多个字段&#xff0c;每个字段具有特定的含义。以下是TCP报文头的主要组成部分&#xff1a; 源端口号&#xff08;Source Port&#xff09;&#xff1a;占用…

C语言 | Leetcode C语言题解之第445题两数相加II

题目&#xff1a; 题解&#xff1a; struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){int stack1[100];int stack2[100];int top1 0;int top2 0;int carry 0;int sum 0;struct ListNode* temp NULL;struct ListNode* head NULL;while (l1) {…

有关若依菜单管理的改造

导言&#xff1a; 搞了个后端对接若依前端&#xff0c;对接菜单管理时候懵懵的就搞完了&#xff0c;也是搞了很久。记一下逻辑和要注意的东西&#xff0c;以后做想似的能有个改造思路。 逻辑&#xff1a; 主要是要把后端传过的数组列表做成类似 这样的&#xff0c;所以要转格式…

git工具指令

下面是常用的Git命令清单&#xff0c;几个专用名称的译名如下&#xff1a; Workspace &#xff1a;工作区 Index /Stage&#xff1a;暂存区 Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09; Remote&#xff1a;远程仓库新建代码库 在当前目录新建一个Git代…

如何在银河麒麟操作系统中查看内存页大小

如何在银河麒麟操作系统中查看内存页大小 1、操作步骤2、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在操作系统中&#xff0c;内存页大小&#xff08;Page Size&#xff09;是一个重要的概念&#xff0c;它决定了操作系统如何…

GPT理论

1.GPT发展 Transformer是一个用作翻译任务的模型&#xff0c;谷歌出品。 GPT全称 lmproving Language Understanding by Generative Pre-Training&#xff0c;用预训练语言理解模型。OPENAI出品。 BERT全称Pre-training of Deep BidirectionalTransformers for Language Unde…

深度学习反向传播-过程举例

深度学习中&#xff0c;一般的参数更新方式都是梯度下降法&#xff0c;在使用梯度下降法时&#xff0c;涉及到梯度反向传播的过程&#xff0c;那么在反向传播过程中梯度到底是怎么传递的&#xff1f;结合自己最近的一点理解&#xff0c;下面举个例子简单说明&#xff01; 一、…

828华为云征文|部署个人知识管理系统 SiyuanNote

828华为云征文&#xff5c;部署个人知识管理系统 SiyuanNote 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 SiyuanNote3.1 SiyuanNote 介绍3.2 SiyuanNote 部署3.3 Siyua…

WebSocket实现在线聊天室

项目实现源码&#xff1a; 前端源码 后端源码 1.常见的消息推送方式 1.1 轮询 1.1.1 轮询的概念 客户端以固定的事件间隔&#xff08;例如每秒或几分钟&#xff09;向服务器发送HTTP请求&#xff0c;服务器收到请求后&#xff0c;处理请求并返回数据给客户端 轮询具体实现htt…