侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

go + websocket实现即时通信之私聊/群聊功能示例

Go  /  管理员 发布于 1年前   662

公司项目中后面需要开发一个即时通讯的聊天功能,

所以我先自己写一个简单的demo ,以实现效果为主, 后面在根据业务场景调整之.


还是那套环境

go + gin + websocket


废话不多少直接上代码:


数据库设计一个简单的表:

CREATE TABLE `chat_sls` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `user_id` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'from 用户',
  `to` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'to  用户',
  `type` tinyint DEFAULT NULL,
  `text` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT 'msg',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='chat';


model:

package model

import (
   "time"
   "gorm.io/gorm"
)

var (
   ChatSlAll = []string{"id", "user_id", "to", "type", "text", "created_at"}
)
type ChatSl struct {
   Model
   UserId    string `json:"userId,omitempty" gorm:"comment:from用户id"`
   To        string `json:"to,omitempty" gorm:"comment:to用户id"`
   Type      int8   `json:"type" gorm:"comment:聊天类型 2私聊"`
   Text      string `json:"text,omitempty" gorm:"comment:msg;default:''`
   CreatedAt time.Time
   UpdatedAt time.Time      `json:"updateAt,omitempty" gorm:"autoUpdateTime:false"`
   DeletedAt gorm.DeletedAt `gorm:"index"`
}


service:

package service

import (
   "zongscan-go/model"
)

type ChatSlService struct {
   Property
}

func NewChatSlService() *ChatSlService {
   return &ChatSlService{
       Property: Property{
           DB: SqlDB(),
       },
   }
}

// 创建
func (s *ChatSlService) Create(m *model.ChatSl) (err error) {
   err = s.DB.Create(m).Error
   return
}

// 查询私聊记录
func (s *ChatSlService) GetSlList(userId string, to string) (list []model.ChatSl, err error) {
   err = s.DB.Select(model.ChatSlAll).Where("user_id = ? and `to` = ?", userId, to).Order("created_at DESC").Limit(10).Find(&list).Error
   return
}


路由:

package router

import (
   v1 "zongscan-go/api/v1"
   "github.com/gin-gonic/gin"
)

func InitChatRouter(r *gin.RouterGroup) {
   g := r.Group("chat")
   {
       g.GET("addsl", v1.WebSocketHandler)
       g.GET("getsllist", v1.GetChatSllist)
   }
}



api控制器:

package v1

import (
"encoding/json"
"fmt"

"zongscan-go/model"
"zongscan-go/model/resp"
"zongscan-go/service"

//"strconv"
"sync"

"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)

// websocket配置
var clients = make(map[string]*websocket.Conn)
var clientMutex sync.Mutex

func WebSocketHandler(c *gin.Context) {
userID := c.Query("userID")
fmt.Println("用户id", userID)
ws, err := websocket.Upgrade(c.Writer, c.Request, nil, 1024, 1024)
if err != nil {
panic(err)
}

// 加入新客户端
clientMutex.Lock()
clients[userID] = ws // 将新客户端添加到客户端集合中
clientMutex.Unlock()
// 处理WebSocket消息
go handleWebSocketMessages(userID, ws) // 开启一个 goroutine 处理该客户端的消息
}

func handleWebSocketMessages(userID string, ws *websocket.Conn) {
for {
messageType, p, err := ws.ReadMessage() // 阻塞等待读取客户端发送的消息
fmt.Println("messageType", messageType, "p", string(p), "clients", clients)

if err != nil {
break // 无法读取信息,退出循环
}
// 解析消息,识别目标客户端或群组
// 根据消息内容选择性发送给特定客户端或群组
// 例如,如果消息格式为私聊消息,则根据消息中指定的目标用户ID发送给特定客户端
// 如果消息格式为群聊消息,则将消息发送给所有群组成员
//recipientIDs := []string{"1", "2"} // 限定发送人群

// 解析消息,这里假设消息是JSON格式,包含"to"字段指定接收者ID
//var msg Message
msg := model.ChatSl{}
err = json.Unmarshal(p, &msg)
if err != nil {
fmt.Println("Error parsing message:", err)
continue
}
//from id
msg.UserId = userID
//fmt.Println("msg:", msg)

// 检查目标用户ID是否合法
if msg.To == "" || msg.To == userID {
fmt.Println("Invalid recipient ID or self-messaging detected")
continue
}

var recipientIDs []string // 在这里声明变量,但不初始化
//私聊 type= 2
if msg.Type == 2 {
recipientIDs = []string{userID, msg.To} // 直接赋值,无需再次声明

//入库
err = service.NewChatSlService().Create(&msg)
if err != nil {
fmt.Println("type=1 add error")
continue
}
} else {
//群聊
// 创建一个空的整数切片来存储键  用户群
recipientIDs = make([]string, 0, len(clients)) // 初始化切片,并分配初始容量

// 遍历map,并将键添加到切片中
for recipientID := range clients {
recipientIDs = append(recipientIDs, recipientID)
}
}

fmt.Println("recipientIDs", recipientIDs)
broadcastMessage(recipientIDs, messageType, p) // 广播消息给所有客户端
}

clientMutex.Lock()
defer clientMutex.Unlock()

delete(clients, userID) // 删除客户端连接
ws.Close()              // 关闭 WebSocket 连接
}

func broadcastMessage(recipientIDs []string, messageType int, p []byte) {
clientMutex.Lock()
defer clientMutex.Unlock()

// 遍历所有客户端连接
for id, client := range clients {
// 检查当前客户端是否在接收者列表中  -- 私聊功能
for _, recipientID := range recipientIDs {
if id == recipientID {
// 向当前客户端发送消息
err := client.WriteMessage(messageType, p)
if err != nil {
// 发送消息失败,关闭连接并删除客户端
client.Close()
delete(clients, id)
}
break
}
}
}
}

//历史记录
func GetChatSllist(c *gin.Context) {
var param model.ChatSl
err := c.ShouldBindJSON(&param)
if err != nil {
fmt.Println("GetChatSllist error")
return
}

list, err := service.NewChatSlService().GetSlList(param.UserId, param.To)
if err != nil {
resp.ERRWithMsg(c, resp.USER_FIND_ERR, err.Error())
return
}

resp.OkWithData(list, c)
}

看看效果

用户1:

1.png

用户2:

2.png

入库数据:

3.png

简单分享一下, 有兴趣可自行测试.


  • 上一条:
    go + websocket实现并发下载文件及中途取消下载功能示例
    下一条:
    程序员兼职引起纠纷之如何安全兼职?
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2017-09
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-12
    • 2021-01
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-08
    • 2024-11
    • 2025-02
    • 2025-04
    • 2025-05
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客