客户端:Vue3,服务端:Node,基于Socket.IO实现单聊的功能

本文主要是介绍客户端:Vue3,服务端:Node,基于Socket.IO实现单聊的功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.介绍

2.环境搭建

3.本功能实现的主要逻辑

4.客户端和服务端的主要代码

5.效果展示

6.socket.io的运作原理


1.介绍

本篇主要讲讲基于Socket.IO实现单聊功能的主要实现,包括了客户端和服务端Node。

在这个即时通讯无处不在的时代,实时聊天功能已经成为我们日常生活和工作中不可或缺的一部分。无论是与朋友闲聊,还是与同事协作,单聊功能都是构建任何通讯平台的基石。今天,我们将一起探索如何利用前端界的新星——Vue 3,以及后端的强大工具——Node.js,结合实时通讯的利器Socket.IO,来打造一个高效、稳定的单聊应用。

在本篇文章中,我们将从零开始,一步步搭建起一个简单的单聊系统。我们会深入了解Vue 3的新特性,如Composition API,以及如何利用它们来构建响应式的用户界面。同时,我们也会探索Node.js的强大功能,以及如何通过Socket.IO实现实时数据的双向传输。

无论你是Vue的新手,还是希望将Socket.IO集成到现有Node.js应用中的开发者,本文都将为你提供实用的指导和深入的见解。我们将一起走过环境搭建、服务端设置、客户端实现、消息格式设计等关键步骤,最终实现一个完整的单聊功能。

2.环境搭建

前端可以使用Vue cli 或者使用 vite都可以,后端使用任何框架也可以,本案例中使用的是express框架。

当然,无论使用哪种方式,我们都得先下载socket.io

npm install socket.io
pnpm add socket.io
yarn add socket.io

或者是使用cdn:socket.io的cnd

然后我们需要搭建一些必要的代码框架,来展示单聊的功能。

在实际的聊天功能中的业务肯定比我这个案例要复杂的多,我这里就是只做一个demo,没有数据库的一些操作,就是实现单聊(单发信息)的功能。

客户端的设计:

  • 我们可以实时的看到所有在线的用户,(聊天肯定有一个username,或者id,我们的这个username主要通过url中的query参数获取,这样可以不用操作数据库)
  • 可以给单独某个用户发送信息,页面会渲染发送的信息详情。

服务端设计:

  • 可以给客户端提供连接socket
  • 可以实时的存储当前在线的用户并发送给客户端
  • 可以接收消息给单独某个用户发送

3.本功能实现的主要逻辑

单聊是这篇文章的主要功能,其实其他很多的连接,是否断连,推送消息,接收消息等等功能从socket.io的官网的基础教程中就可以找到,主要是这个单聊的逻辑。

我们可以在客户端建一个user的数组,只要一有人连接,就往当中存储必要的username和userid,(注意这里的id是socket.io连接时创建的,每次都不唯一),如果有重连的就替换成新的id,有断连的就从当中剔除,然后再接收数据时可以查找到对应的id,给其单独的推送消息即可。

let userList = [] 
//监听接收消息的对象socket.on('sendMessage', (data) => {const targetSocket = io.sockets.sockets.get(data.id)const targetUser = userList.find((user) => user.id === data.id)if (targetSocket) {targetSocket.emit('receiveMessage', {sendForm: data.sendForm,toUser: targetUser.username,message: data.message,date: new Date().getTime()})}})

我们主要的还是通过id去进行单独推送信息。

然后客户端可以监听此信息,去接受服务端发来的数据

//监听接收信息socket.on("receiveMessage", (data) => {messageList.value.push(data)})

4.客户端和服务端的主要代码

客户端:

<template><div><!-- <router-view></router-view> --><button @click="createWebSocketConnection">建立Websocket连接</button><button @click="close">主动断开连接</button><ul><li v-for="i in userList" :key="i.id" @click="chooseUser = i.username, chooseid = i.id":class="{ active: i.username === chooseUser }">{{ i.username }}</li></ul><div class="text"><input type="text" v-model="message" @focus="handleFocus" @blur="handleBlur"><button @click="sendMessage">发送信息</button></div><h1>{{ typing }}</h1><!-- 聊天内容 --><div class="chat"><ul><li v-for="i in messageList" :key="i.id"><p>发送者:{{ i.sendForm }} ,接收者:{{ i.toUser }},内容:{{ i.message }}</p></li></ul></div></div>
</template><script setup>
import { io } from "socket.io-client";
import { useRoute } from "vue-router";
import { onUnmounted, ref } from 'vue'
const $route = useRoute()
const userList = ref([])
const message = ref('')
const messageList = ref([])
//选择当前需要发送信息的对象
const chooseUser = ref('')
const chooseid = ref('')
//谁正在输入中
const typing = ref('')
let socket = null
const createWebSocketConnection = () => {//随机生成一个usernamesocket = io("http://localhost:5000", {query: { username: $route.query.username },});socket.on('connect', () => {console.log('连接成功')console.log(socket.id);})socket.on("disconnect", () => {console.log('断开连接'); // undefined});socket.on("userList", (data) => {userList.value = data})//监听接收信息socket.on("receiveMessage", (data) => {messageList.value.push(data)})socket.on('receiveTyping', (data) => {console.log(data);typing.value = data.message})socket.on('receiveStopTyping', (id) => {typing.value = ''})
}
const closeWebSocket = () => {socket.close()
}
const sendMessage = () => {//发送信息const messageData = {//发送对象sendForm: $route.query.username,//接受对象receiveForm: chooseUser.value,//时间time: new Date(),id: chooseid.value,message: message.value}socket.emit("sendMessage", messageData)
}
//监听用户正在输入中
const handleFocus = () => {const data = {username: $route.query.username,tousername: chooseUser.value,id: chooseid.value,message: `${$route.query.username} 正在输入中...`}socket.emit('typing', data)
}
//监听用户停止输入
const handleBlur = () => {socket.emit('stopTyping', chooseid.value)
}
const close = () => {socket.emit('close', $route.query.username)closeWebSocket()
}
onUnmounted(() => {close()
})</script><style lang="css" scoped>
.active {color: red;
}
</style>

服务端:

const express = require('express')const app = express()
const { Server } = require('socket.io')
app.get('/', (req, res) => {res.send('Hello World!')
})//创建websocket服务器
const io = new Server(5000, {cors: {origin: '*' //配置跨域}
})
let userList = []//监听事件
io.on('connection', (socket) => {console.log('a user connected')const username = socket.handshake.query.usernameif (!username) {return}const userinfo = userList.find((user) => user.username === username)if (userinfo) {//存在userinfo.id = socket.id} else {userList.push({id: socket.id,username})}console.log(userList)socket.on('disconnect', () => {console.log('user disconnected')})//发送事件io.emit('userList', userList)//监听接收消息的对象socket.on('sendMessage', (data) => {const targetSocket = io.sockets.sockets.get(data.id)const targetUser = userList.find((user) => user.id === data.id)if (targetSocket) {targetSocket.emit('receiveMessage', {sendForm: data.sendForm,toUser: targetUser.username,message: data.message,date: new Date().getTime()})}})//监听断开连接的信息socket.on('close', (username) => {userList = userList.filter((user) => user.username !== username)console.log(userList)io.emit('userList', userList)})//监听谁正在输入的事件socket.on('typing', (data) => {const targetSocket = io.sockets.sockets.get(data.id)if (targetSocket) {targetSocket.emit('receiveTyping', data)}})//监听停止输入的事件socket.on('stopTyping', (id) => {const targetSocket = io.sockets.sockets.get(id)if (targetSocket) {targetSocket.emit('receiveStopTyping')}})
})app.listen(3000)

5.效果展示

效果是显而易见的,整体实现了单聊的效果。

6.socket.io的运作原理

Socket.IO 的运作原理涉及到几个关键的技术和概念,以实现实时的双向通信。以下是 Socket.IO 的基本运作原理:

1. 基于 WebSocket 的通信

  • WebSocket 是一种提供全双工通信通道的协议,允许服务器和客户端之间进行实时数据交换。Socket.IO 在底层默认使用 WebSocket 作为传输层,因为它提供了最快和最直接的通信方式。

2. 自动传输升级

  • 当客户端尝试与服务器建立连接时,Socket.IO 会首先尝试使用 WebSocket。如果浏览器或网络环境不支持 WebSocket,Socket.IO 会自动降级到其他可用的传输方式,如长轮询(Long-Polling)。

3. 事件驱动的接口

  • Socket.IO 提供了一个事件驱动的接口,允许开发者在客户端和服务器端监听和发射(emit)事件。这意味着除了简单的数据传输,Socket.IO 还支持复杂的交互模式,如实时游戏、协作编辑等。

4. 心跳包

  • 为了维持连接的活跃状态并检测连接是否仍然有效,Socket.IO 定期在客户端和服务器之间发送心跳包。如果在一定时间内没有收到响应,Socket.IO 会认为连接已断开,并尝试重新连接。

5. 房间和命名空间

  • Socket.IO 允许创建房间(rooms)和命名空间(namespaces),以便对通信进行分组和隔离。这样,不同的用户可以被添加到不同的房间,或者在不同的命名空间中通信,而不会互相干扰。

6. 广播和去中心化通信

  • 服务器可以向所有连接的客户端广播消息,也可以向特定的客户端或房间发送消息。此外,Socket.IO 支持去中心化通信,客户端可以直接向其他客户端发送消息,而无需通过服务器中转。

7. 断开重连

  • 如果客户端与服务器的连接断开,Socket.IO 会尝试自动重连。它会尝试使用不同的传输方式,直到重新建立连接。这个过程对用户来说是透明的,不会影响应用的实时性。

8. 状态管理

  • Socket.IO 管理每个连接的状态,包括连接、断开、重连等。服务器和客户端都可以监听这些状态变化,并根据状态执行相应的操作。

通过这些机制,Socket.IO 能够在各种网络环境下提供稳定的实时通信能力,使其成为构建实时应用的强大工具。开发者可以专注于业务逻辑的实现,而不必过多担心底层的通信细节。

具体的可以查看官方文档:socket.io的运作原理

这篇关于客户端:Vue3,服务端:Node,基于Socket.IO实现单聊的功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/818123

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo