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

news2024/11/16 21:50:46

这里我使用的后端 基于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>
        <li
          v-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">
        <div
          v-for="(msg, index) in messages"
          :key="index"
          :class="{ sent: msg.sentByUser, received: !msg.sentByUser }"
        >
          {{ msg.text }} <span class="timestamp">{{ msg.timestamp }}</span>
        </div>
      </div>

      <input
        v-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.coloradmin.cn/o/2159864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pointnet++改进59:全网首发MogaBlock(2024最新模块)|用于在纯基于卷积神经网络的模型中进行判别视觉表示学习,具有良好的复杂性和性能权衡

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入MogaBlock,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.…

GreenPlum与PostgreSQL数据库

*** Greenplum*** 是一款开源数据仓库。基于开源的PostgreSQL改造&#xff0c;主要用来处理大规模数据分析任务&#xff0c;相比Hadoop&#xff0c;Greenplum更适合做大数据的存储、计算和分析引擎 它本质上是多个PostgreSQL面向磁盘的数据库实例一起工作形成的一个紧密结合的数…

微软宣布弃用面向企业的WSUS更新服务 仍然保留该服务但不再添加任何新功能

Windows Server Update Services 是微软面向企业推出的一项更新服务&#xff0c;该服务已经存在很多年&#xff0c;允许 IT 管理员控制内网设备的更新节奏。今年早些时候微软宣布将在 2025 年 4 月 18 日开始弃用 WSUS 驱动程序同步功能&#xff0c;因为大约只有 1/3 的 IT 管理…

生成PPT时支持上传本地的PPT模板了!

制作 PPT 时想要使用特定的 PPT 模板&#xff1f; 现在&#xff0c;歌者 PPT 的「自定义模板功能」已全面升级&#xff01;你可以轻松上传自己的本地 PPT 模板&#xff0c;无论是公司统一风格的模板&#xff0c;还是带有个人设计风格的模板&#xff0c;都能无缝导入歌者 PPT。…

单链表:学生信息管理系统

一、头文件 #ifndef __LINK_H__ #define __LINK_H__ #include <myhead.h> #define MAX 30 // 建立学生结构体 typedef struct student {int id; //学号char name[20]; //姓名float score; //分数 }stu;typedef struct node {union{int len;stu data;};struct node * nex…

玄机--哥斯拉流量

1、黑客的ip 过滤http&#xff0c;观察哪个ip一直在发送请求包 2、什么漏洞 因为是哥斯拉&#xff0c;那么黑客在连接成功之前一定上传这个木马文件到服务端 hello.jsp是木马文件&#xff0c;过滤http contains “hello.jsp” 最早是PUT这个方法 3、文件名 hello.jsp 保存…

让AI激发创作力:OpenAI分享5位专业作家利用ChatGPT写作的案例技巧

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

GPU硬件如何实现光栅化?

版权声明 本文为“优梦创客”原创文章&#xff0c;您可以自由转载&#xff0c;但必须加入完整的版权声明文章内容不得删减、修改、演绎本文视频版本&#xff1a;见文末 引言 大家好&#xff0c;我是老雷&#xff0c;今天我想从GPU硬件原理出发&#xff0c;给大家分享在图形渲…

JS防抖和节流函数

节流和防抖函数的定义 防抖&#xff1a;只有在最后一次事件发生后的一定时间间隔内没有新的事件触发时&#xff0c;才执行相应的处理函数。节流&#xff1a;在规定的时间间隔内最多只能执行一次相应的处理函数。 效果图 示例图 示例图运行结果如下&#xff1a; 代码 &l…

远程升级,你成功了吗?

最近又遇到了远程升级失败的情况&#xff0c;而且是不明原因的多次接连失败。。。 事情是这样的&#xff1a;最近有客户反馈在乡村里频繁出现掉线的情况。通过换货、换SIM卡对比排查测试&#xff0c;发现只有去年5月22号采购的那批模块在客户环境附近会出现掉线的情况&#xf…

【深度学习】03-神经网络3-1梯度下降网络优化方法

每一条线是一个权重&#xff0c;每个神经元由一个加权和还有一个 激活函数组成。每一层可以理解是一个函数&#xff0c;最终形成一个复合函数&#xff0c;因此求梯度的时候&#xff0c;是一层一层的求解&#xff0c;所以叫做反向传播。 只会考虑当前数据之前的数据&#xff0c;…

潮玩宇宙大逃杀宝石游戏搭建开发

潮玩宇宙大逃杀的开发主要涉及以下方面&#xff1a; 1. 游戏概念和设计&#xff1a; 核心概念定义&#xff1a;确定以潮玩为主题的宇宙背景、游戏的基本规则和目标。例如&#xff0c;玩家在宇宙场景中参与大逃杀竞技&#xff0c;目标是成为最后存活的玩家。 玩法模式设计&a…

k8s上安装prometheus

一、下载对应的kube-prometheus源码 github地址&#xff1a;GitHub - prometheus-operator/kube-prometheus: Use Prometheus to monitor Kubernetes and applications running on Kubernetes 1&#xff09;进入目录 [rootk8s-master ~]# cd kube-prometheus [rootk8s-master…

商城小程序源码搭建部署,商城购物小程序开发流程(php框架)

关于商城小程序 商城小程序作为一种基于移动互联网的在线购物平台&#xff0c;商家可以上架所销售的产品&#xff0c;定价&#xff0c;以及营运营的在线售货平台。买家无需下载应用&#xff0c;在小程序搜索打开即可浏览下单商品。 技术栈 前端: vue uniapp 后端&#xff1a…

嵌入式Linux学习笔记(7)-Socket网络编程

一、什么是Socket网络编程 Socket是一种抽象的编程接口&#xff0c;可以用于在不同主机之间进行数据通信。Socket API提供了一系列函数来创建、连接、发送和接收数据等操作。嵌入式 Linux 系统中的 Socket 网络编程是指在嵌入式系统中使用 Socket API 进行网络通信。 Socket 网…

HTTP协议1.1请求头和keep-alive

请求头分类 End-to-end&#xff08;端对端&#xff09; 必须全部带给目标服务器&#xff0c;不会被中途变化或去掉 Hop-by-hop&#xff08;逐跳头&#xff09; 比如客户端发请求&#xff0c;要路过代理(例如Nginx)&#xff0c;头可以被自动删掉&#xff0c;来到真正服务器上…

vue/配置axios(前后端数据连通/api接口的调用)

1.创建apis文件 2.写入调用的api地址且暴露出去。 import httpInstance from /utils/http;export function getHomeNav() {return httpInstance({url: http://10.0.11.91:91/dailyreport/getdailyreportall,}) }3.创建文件编写拦截器 代码部分 //axios基础封装 import axio…

Thinkphp5x远程命令执行 靶场攻略

环境配置 靶场&#xff1a;vulhub/thinkphp/5-rce docker-compose up -d #启动环境 漏洞复现 1.访问靶场&#xff1a;http://172.16.1.198:8080/ 2.远程命令执⾏ POC&#xff1a; ?sindex/think\app/invokefunction&functioncall_user_func_array&vars[0]system…

Bytebase 2.23.0 - 支持 Entra (Azure AD) 用户/组同步

&#x1f680; 新功能 支持从 Entra ID&#xff08;前 Azure AD&#xff09;同步用户和群组。 支持 CockroachDB。 支持项目级别的默认备份设置&#xff0c;包含自动启用和跳过错误选项。 SQL 编辑器支持实时语法检查。 支持配置密码限制策略。 &#x1f514; 重大变更 分类…

初试AngularJS前端框架

文章目录 一、框架概述二、实例演示&#xff08;一&#xff09;创建网页&#xff08;二&#xff09;编写代码&#xff08;三&#xff09;浏览网页&#xff08;四&#xff09;运行结果 三、实战小结 一、框架概述 AngularJS 是一个由 Google 维护的开源前端 JavaScript 框架&am…