Go语言开发im-websocket服务和vue3+ts开发类似微信pc即时通讯
前言
IM即时通讯聊天, 为软件开发者打造,不依赖第三方sdk,完全用Go语言开发即时通讯服务,支持H5、Electron、Wails 、Uniapp和各种小程序的IM即时通讯, 快速实现私聊、群聊、在线客服!让你快速搭建一个微信聊天系统,打造一个类微信聊天应用。
内容如下:
- 完全基于GoFly框架开发即时通讯服务器,不依赖第三方即时通讯SDK,减少维护成本。
- 支持gofly管理后台、H5、Electron、Wails 、Uniapp和各种小程序的IM即时通讯
- 一对一单聊
- 群聊
- 在线客服
- 发送内容支持文本、图片、附件(zip、text、word...)音频、视频等
后端选择技术栈:
- 开发语言:Golang
- 基础框架:Gin
- 集成框架:GoFly快速开发框架
- 数据库:mysql(可迁移PostgreSQL、SQL-Server、oracle)
前端选择技术栈:
-
脚手架搭建:vite
-
web框架:vue3
-
前端语言:TypeScript
-
前端UI:ArcoDesign
通讯协议:
即时通讯协议:websocket,通讯核心代码如下:
- Go服务端代码:
package websocketimport ("fmt""net/http""sync""time""github.com/gorilla/websocket"
)var (// 消息通道news = make(map[string]chan interface{})// websocket客户端链接池client = make(map[string]*websocket.Conn)// 互斥锁,防止程序对统一资源同时进行读写mux sync.Mutex
)// websocket Upgrader
var wsupgrader = websocket.Upgrader{ReadBufferSize: 1024,WriteBufferSize: 1024,HandshakeTimeout: 5 * time.Second,// 取消ws跨域校验CheckOrigin: func(r *http.Request) bool {return true},
}// WsHandler 处理ws请求
func WsHandler(w http.ResponseWriter, r *http.Request, id string) {var conn *websocket.Connvar err errorvar exist bool// 创建一个定时器用于服务端心跳pingTicker := time.NewTicker(time.Second * 10)conn, err = wsupgrader.Upgrade(w, r, nil)if err != nil {fmt.Println("处理ws请求错误", err)return}// 把与客户端的链接添加到客户端链接池中addClient(id, conn)// 获取该客户端的消息通道m, exist := getNewsChannel(id)if !exist {m = make(chan interface{})addNewsChannel(id, m)}// 设置客户端关闭ws链接回调函数conn.SetCloseHandler(func(code int, text string) error {deleteClient(id)fmt.Println("端关闭ws链接回调函数错误", code)return nil})for {select {case content, _ := <-m:// 从消息通道接收消息,然后推送给前端fmt.Println("从消息通道接收消息:", content)err = conn.WriteJSON(content)if err != nil {fmt.Println("推送给前端数错误", err)conn.Close()deleteClient(id)return}case <-pingTicker.C:// 服务端心跳:每20秒ping一次客户端,查看其是否在线conn.SetWriteDeadline(time.Now().Add(time.Second * 20))err = conn.WriteMessage(websocket.PingMessage, []byte{})if err != nil {fmt.Println("send ping err:", err)conn.Close()deleteClient(id)return}}}
}// 将客户端添加到客户端链接池
func addClient(id string, conn *websocket.Conn) {mux.Lock()client[id] = connmux.Unlock()
}// 获取指定客户端链接
func getClient(id string) (conn *websocket.Conn, exist bool) {mux.Lock()conn, exist = client[id]mux.Unlock()return
}// 删除客户端链接
func deleteClient(id string) {mux.Lock()delete(client, id)fmt.Println("websocket退出:", id)mux.Unlock()
}// 添加用户消息通道
func addNewsChannel(id string, m chan interface{}) {mux.Lock()news[id] = mmux.Unlock()
}// 获取指定用户消息通道
func getNewsChannel(id string) (m chan interface{}, exist bool) {mux.Lock()m, exist = news[id]mux.Unlock()return
}// 删除指定消息通道
func deleteNewsChannel(id string) {mux.Lock()if m, ok := news[id]; ok {close(m)delete(news, id)}mux.Unlock()
}// 1.对点消息推送
func SetMessage(id string, content interface{}) {mux.Lock()if m, exist := news[id]; exist {go func() {m <- content}()}mux.Unlock()
}// 2.群发消息
func SetMessageAllClient(content interface{}) {mux.Lock()all := newsmux.Unlock()go func() {for _, m := range all {m <- content}}()}
- 前端ts代码:
// WebSocket链接工具
import { onUnmounted } from 'vue';interface WebSocketOptions {url: string;protocols?: string | string[];reconnectTimeout?: number;
}class WebSocketService {private ws: WebSocket | null = null;private callbacks: { [key: string]: Function[] } = {};private reconnectTimeoutMs: number = 5000; // 默认5秒重连间隔constructor(private options: WebSocketOptions) {}//实现断线重连private reconnectAttempts = 0;private maxReconnectAttempts = 5;public open(): void {if(!this.ws){this.ws = new WebSocket(this.options.url, this.options.protocols)this.ws.addEventListener('open', this.handleOpen);this.ws.addEventListener('message', this.handleMessage);this.ws.addEventListener('error', this.handleError);this.ws.addEventListener('close', this.handleClose);//为了保持连接的稳定性,我们可以添加心跳机制this.startHeartbeat();}}//连接public connect(url:any): void {if(url){this.options.url=urlthis.open();}else{console.error("请传url链接地址")}}public close(isActiveClose = false): void {if (this.ws) {this.ws.close();if (!isActiveClose) {setTimeout(() => this.reconnect(), this.reconnectTimeoutMs);}}}//重连public reconnect(): void {if (this.reconnectAttempts < this.maxReconnectAttempts) {this.reconnectAttempts++;console.log(`尝试重新连接... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);setTimeout(() => {this.open();}, this.reconnectTimeoutMs);} else {console.error('达到最大重连次数,连接失败');}}public on(event: 'message', callback: (data: any) => void): void;public on(event: 'open' | 'error' | 'close', callback: () => void): void;public on(event: string, callback: (...args: any[]) => void): void {if (!this.callbacks[event]) {this.callbacks[event] = [];}this.callbacks[event].push(callback);}private handleOpen = (): void => {console.log('WebSocket连接已建立');if (this.callbacks.open) {this.callbacks.open.forEach((cb) => cb());}//实现断线重连this.reconnectAttempts = 0;this.startHeartbeat();};private handleMessage = (event: MessageEvent): void => {const data = JSON.parse(event.data);// console.log('WebSocket接收到消息:', data);if (this.callbacks.message) {this.callbacks.message.forEach((cb) => cb(data));}};private handleError = (error: Event): void => {console.error('WebSocket错误:', error);if (this.callbacks.error) {this.callbacks.error.forEach((cb) => cb(error));}};private handleClose = (): void => {console.log('WebSocket连接已关闭');//实现断线重连this.ws=nullthis.stopHeartbeat();this.reconnect();if (this.callbacks.close) {this.callbacks.close.forEach((cb) => cb());if (!this.options.reconnectTimeout) {this.reconnect();}}};public send(data: any): void {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify(data));} else {console.warn('尝试发送消息时WebSocket未连接');}}//添加心跳机制private heartbeatTimer: any | null = null;private heartbeatInterval = 30000; // 30秒private startHeartbeat() {this.heartbeatTimer = setInterval(() => {this.send({ type: 'heartbeat' });}, this.heartbeatInterval);}private stopHeartbeat() {if (this.heartbeatTimer) {clearInterval(this.heartbeatTimer);this.heartbeatTimer = null;}}}export default function useWebSocket(options: WebSocketOptions) {const wsService = new WebSocketService(options);onUnmounted(() => {wsService.close(true);});return {open: wsService.open.bind(wsService),connect: wsService.connect.bind(wsService),close: wsService.close.bind(wsService),reconnect: wsService.reconnect.bind(wsService),on: wsService.on.bind(wsService),send: wsService.send.bind(wsService)};}
效果示例
完整代码下载
去下载完整代码
相关文章:

Go语言开发im-websocket服务和vue3+ts开发类似微信pc即时通讯
前言 IM即时通讯聊天, 为软件开发者打造,不依赖第三方sdk,完全用Go语言开发即时通讯服务,支持H5、Electron、Wails 、Uniapp和各种小程序的IM即时通讯, 快速实现私聊、群聊、在线客服!让你快速搭建一个微信聊天系统,打…...

Redis如何实现分布式锁
目录 获取锁: 释放锁: Lua脚本: Redisson 分布式锁是,满足分布式系统或集群模式下多进程可见并且互斥的锁,因为我们熟知的java中的锁只是在单体架构下单个jvm中才会生效,如果部署了多个jvm则会导致新的…...

面向对象程序设计之继承(C++)
1.继承的定义 1.1继承的概念 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承 呈现了⾯向…...

IAPP发布《2024年人工智能治理实践报告》
文章目录 前言一、黑箱问题►透明度、可理解性与可解释性二、法律和政策中的注意事项►欧盟的《通用数据保护条例》►欧盟的AI法案►NIST的AI风险管理框架►美国的第14110号行政命令►《生成式人工智能服务管理暂行办法》►新加坡的AI验证三、实施人工智能治理►模型卡与系统卡…...

了解MySQL 高可用架构:主从备份
为了防止数据库的突然挂机,我们需要对数据库进行高可用架构。主从备份是常见的场景,通常情况下都是“一主一从/(多从)”。正常情况下,都是主机进行工作,从机进行备份主机数据,如果主机某天突然意外宕机,从机…...

[OpenCV] 数字图像处理 C++ 学习——15像素重映射(cv::remap) 附完整代码
文章目录 前言1.像素重映射理论基础2.代码实现(1) remap()细节(2)水平翻转(2)垂直翻转(3)旋转 180 度(4)径向扭曲 3.完整代码 前言 像素重映射将图像中的每个像素映射到新位置,实现图像的扭曲、校正等操作。在 OpenCV 中,cv::remap() 函数就是用于实现这…...
Oreace每日运维操作
一.Oreace每日运维操作 目录 一.Oreace每日运维操作 1.1、确认所有的INSTANCE状态正常 1.2、检查文件系统的使用(剩余空间) 1.3 lwh暗码,,、检查日志文件和trace文件记录 1.4 lwh、检查数据库当日备份…...

【XR】AR HUD
1. AR HUD(head up display)原理 目标: 产业链上的各大Tier1及PGU企业都在积极开发这一技术,许多厂家已推出LCOS样机,比如说水晶光电、华阳集团、瀚思通、疆程已在北京车展或去年的上海车展上展出了LCOS方案的AR-HUD样…...
C/C++内存管理——内存泄漏/内存碎片
一、什么是内存泄漏 内存泄漏指的是在程序运行过程中,已经分配给程序使用的内存没有得到及时和正确的释放,导致这部分内存无法被程序再次使用或者被操作系统回收。内存泄漏通常发生在动态分配的内存上,如果这部分内存没有被正确释放,随着时间的推移,越来越多的内存将被占…...

使用 GaLore 预训练LLaMA-7B
项目代码: https://github.com/jiaweizzhao/galorehttps://github.com/jiaweizzhao/galore 参考博客: https://zhuanlan.zhihu.com/p/686686751 创建环境 基础环境配置如下: 操作系统: CentOS 7CPUs: 单个节点具有 1TB 内存的 Intel CP…...
gitlab无法push(pre-receive hook declined)
如果是个人的项目,托管在官网,可以参考这位大佬的, GitLab新建项目后push reject提交失败的解决办法_push rejected-CSDN博客 如果是公司的项目,去项目成员里看自己的身份,如果只是developer,是无法push到…...

物品识别——基于python语言
目录 1.物品识别 2.模型介绍 3.文件框架 4.代码示例 4.1 camera.py 4.2 interaction.py 4.3 object_detection.py 4.4 main.py 4.5 运行结果 5.总结 1.物品识别 该项目使用Python,OpenCV进行图像捕捉,进行物品识别。我们将使用YOLO(…...

【PostgreSQL】安装及使用(Navicat/Arcgis),连接(C#)
简介 PostgreSQL 是一个功能强大的开源对象关系数据库系统 下载地址 PostgreSQL: Downloads 由于我电脑上安装的是arcgispro3.1所以需要下载对应的postgresql版本 PostgreSQL 12 对应的 PostGIS 版本主要是 3.5.0 或更高版本。 安装 一般设置为postgresql 安装扩展插件 此…...

第L6周:机器学习-随机森林(RF)
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 目标: 1.什么是随机森林(RF) 随机森林(Random Forest, RF)是一种由 决策树 构成的 集成算法 &#…...

【电路笔记】-差分运算放大器
差分运算放大器 文章目录 差分运算放大器1、概述2、差分运算放大器表示2.1 差分模式2.2 减法器模式3、差分放大器示例3.1 相关电阻3.2 惠斯通桥3.3 光/温度检测4、仪表放大器5、总结1、概述 在之前的文章中,我们讨论了反相运算放大器和同相运算放大器,我们考虑了在运算放大器…...
git 命令---想要更改远程仓库
在 Git 中,origin 是默认的远程仓库名称。可以使用以下命令查看当前 Git 仓库的 origin 名称及其对应的 URL: git remote -v这个命令会列出所有配置的远程仓库及其名称,其中 origin 通常是克隆时自动设置的默认远程仓库名称。输出示例&#…...
LeetCode:2848. 与车的相交点 一次遍历,时间复杂度O(n)
2848. 与车的相交点 today 2848. 与车的相交点 题目描述 给你一个下标从 0开始的二维整数数组 nums 表示汽车停放在数轴上的坐标。对于任意下标 i ,nums[i] [starti, endi] ,其中 s t a r t i start_i starti 是第 i 辆车的起点, e n …...

Xcode 16 RC (16A242) 发布下载,正式版下周公布
Xcode 16 RC (16A242) - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 请访问原文链接:https://sysin.org/blog/apple-xcode-16/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org Xcode 16 的新功…...

git 更换远程地址的方法
需要将正在开发的代码远程地址改成新的地址,通过查询发现有三个方法可以实现,特此记录。具体方法如下: (1)通过命令直接修改远程仓库地址 git remote 查看所有远程仓库git remote xxx 查看指定远程仓库地址git remote…...

9. 什么是 Beam Search?深入理解模型生成策略
是不是总感觉很熟悉? 在之前第5,7,8篇文章中,我们都曾经用到过与它相关的参数,而对于早就有着实操经验的同学们,想必见到的更多。这篇文章将从示例到数学原理和代码带你进行理解。 Beam Search 对应的中文翻…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...