当前位置: 首页 > 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)};}

效果示例

完整代码下载

去下载完整代码

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • jenkins入门
  • Java集合(八股)
  • 沉浸式利用自然语言无代码开发工具生成式AI产品应用(上)
  • 嵌入式软件工程师:科技浪潮中的关键角色
  • 使用iperf3测试局域网服务器之间带宽
  • 拖拽排序的实现示例demo
  • 物品识别——基于python语言
  • 人工智能GPT____豆包使用的一些初步探索步骤 体验不一样的工作
  • [NSSRound#4 SWPU]hide_and_seek-用gdb调试
  • 【2024】前端学习笔记6-容器标签div
  • 人工智能开发实战matplotlib库应用基础
  • 性能测试-jmeter的控制器(十六)
  • 腾讯云升级多个云存储解决方案 以智能化存储助力企业增长
  • 物联网——DMA+AD多通道
  • Flutter - Win32程序是如何执行main函数
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • [译] React v16.8: 含有Hooks的版本
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • es的写入过程
  • go append函数以及写入
  • javascript从右向左截取指定位数字符的3种方法
  • Linux gpio口使用方法
  • MQ框架的比较
  • React Transition Group -- Transition 组件
  • Spring框架之我见(三)——IOC、AOP
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 关于for循环的简单归纳
  • 基于web的全景—— Pannellum小试
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 前端存储 - localStorage
  • 我从编程教室毕业
  • 学习HTTP相关知识笔记
  • 怎么将电脑中的声音录制成WAV格式
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 走向全栈之MongoDB的使用
  • 《天龙八部3D》Unity技术方案揭秘
  • 阿里云重庆大学大数据训练营落地分享
  • ​数据结构之初始二叉树(3)
  • !!java web学习笔记(一到五)
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • (1) caustics\
  • (1)(1.13) SiK无线电高级配置(六)
  • (6)添加vue-cookie
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (剑指Offer)面试题34:丑数
  • (算法)N皇后问题
  • (小白学Java)Java简介和基本配置
  • .form文件_SSM框架文件上传篇
  • .net 发送邮件
  • .net反编译的九款神器
  • .net通过类组装数据转换为json并且传递给对方接口
  • .net中生成excel后调整宽度
  • .vimrc 配置项