当前位置: 首页 > news >正文

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即时通讯聊天, 为软件开发者打造&#xff0c;不依赖第三方sdk&#xff0c;完全用Go语言开发即时通讯服务&#xff0c;支持H5、Electron、Wails 、Uniapp和各种小程序的IM即时通讯, 快速实现私聊、群聊、在线客服&#xff01;让你快速搭建一个微信聊天系统&#xff0c;打…...

Redis如何实现分布式锁

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

面向对象程序设计之继承(C++)

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

IAPP发布《2024年人工智能治理实践报告》

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

了解MySQL 高可用架构:主从备份

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

[OpenCV] 数字图像处理 C++ 学习——15像素重映射(cv::remap) 附完整代码

文章目录 前言1.像素重映射理论基础2.代码实现(1) remap()细节(2)水平翻转(2)垂直翻转(3)旋转 180 度(4)径向扭曲 3.完整代码 前言 像素重映射将图像中的每个像素映射到新位置&#xff0c;实现图像的扭曲、校正等操作。在 OpenCV 中&#xff0c;cv::remap() 函数就是用于实现这…...

Oreace每日运维操作

一&#xff0e;Oreace每日运维操作 目录 一&#xff0e;Oreace每日运维操作 1.1、确认所有的INSTANCE状态正常 1.2、检查文件系统的使用&#xff08;剩余空间&#xff09; 1.3 lwh暗码&#xff0c;&#xff0c;、检查日志文件和trace文件记录 1.4 lwh、检查数据库当日备份…...

【XR】AR HUD

1. AR HUD&#xff08;head up display&#xff09;原理 目标&#xff1a; 产业链上的各大Tier1及PGU企业都在积极开发这一技术&#xff0c;许多厂家已推出LCOS样机&#xff0c;比如说水晶光电、华阳集团、瀚思通、疆程已在北京车展或去年的上海车展上展出了LCOS方案的AR-HUD样…...

C/C++内存管理——内存泄漏/内存碎片

一、什么是内存泄漏 内存泄漏指的是在程序运行过程中,已经分配给程序使用的内存没有得到及时和正确的释放,导致这部分内存无法被程序再次使用或者被操作系统回收。内存泄漏通常发生在动态分配的内存上,如果这部分内存没有被正确释放,随着时间的推移,越来越多的内存将被占…...

使用 GaLore 预训练LLaMA-7B

项目代码&#xff1a; https://github.com/jiaweizzhao/galorehttps://github.com/jiaweizzhao/galore 参考博客&#xff1a; https://zhuanlan.zhihu.com/p/686686751 创建环境 基础环境配置如下&#xff1a; 操作系统: CentOS 7CPUs: 单个节点具有 1TB 内存的 Intel CP…...

gitlab无法push(pre-receive hook declined)

如果是个人的项目&#xff0c;托管在官网&#xff0c;可以参考这位大佬的&#xff0c; GitLab新建项目后push reject提交失败的解决办法_push rejected-CSDN博客 如果是公司的项目&#xff0c;去项目成员里看自己的身份&#xff0c;如果只是developer&#xff0c;是无法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&#xff0c;OpenCV进行图像捕捉&#xff0c;进行物品识别。我们将使用YOLO&#xff08…...

【PostgreSQL】安装及使用(Navicat/Arcgis),连接(C#)

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

第L6周:机器学习-随机森林(RF)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 1.什么是随机森林&#xff08;RF&#xff09; 随机森林&#xff08;Random Forest, RF&#xff09;是一种由 决策树 构成的 集成算法 &#…...

【电路笔记】-差分运算放大器

差分运算放大器 文章目录 差分运算放大器1、概述2、差分运算放大器表示2.1 差分模式2.2 减法器模式3、差分放大器示例3.1 相关电阻3.2 惠斯通桥3.3 光/温度检测4、仪表放大器5、总结1、概述 在之前的文章中,我们讨论了反相运算放大器和同相运算放大器,我们考虑了在运算放大器…...

git 命令---想要更改远程仓库

在 Git 中&#xff0c;origin 是默认的远程仓库名称。可以使用以下命令查看当前 Git 仓库的 origin 名称及其对应的 URL&#xff1a; git remote -v这个命令会列出所有配置的远程仓库及其名称&#xff0c;其中 origin 通常是克隆时自动设置的默认远程仓库名称。输出示例&#…...

LeetCode:2848. 与车的相交点 一次遍历,时间复杂度O(n)

2848. 与车的相交点 today 2848. 与车的相交点 题目描述 给你一个下标从 0开始的二维整数数组 nums 表示汽车停放在数轴上的坐标。对于任意下标 i &#xff0c;nums[i] [starti, endi] &#xff0c;其中 s t a r t i start_i starti​ 是第 i 辆车的起点&#xff0c; e n …...

Xcode 16 RC (16A242) 发布下载,正式版下周公布

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

git 更换远程地址的方法

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

9. 什么是 Beam Search?深入理解模型生成策略

是不是总感觉很熟悉&#xff1f; 在之前第5&#xff0c;7&#xff0c;8篇文章中&#xff0c;我们都曾经用到过与它相关的参数&#xff0c;而对于早就有着实操经验的同学们&#xff0c;想必见到的更多。这篇文章将从示例到数学原理和代码带你进行理解。 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. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

AI,如何重构理解、匹配与决策?

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

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...