package chat import (
"encoding/json"
"github.com/gorilla/websocket"
"github.com/zeromicro/go-zero/core/logx"
"log"
"net/http"
"sync"
) type Client struct {
conn *websocket.Conn
messageQueue chan []byte
mu sync.Mutex
user string
} func NewClient(user string, conn *websocket.Conn) *Client {
return &Client{
conn: conn,
user: user,
messageQueue: make(chan []byte, 100),
}
} func (c *Client) ReadPump() {
defer func() {
c.conn.Close()
}() for {
mt, message, err := c.conn.ReadMessage()
if err != nil {
log.Println("read:", err)
manager.mu.Lock()
delete(manager.clients, c.user)
_ = c.conn.Close()
manager.mu.Unlock()
break
}
if mt == websocket.TextMessage || mt == websocket.PingMessage {
c.mu.Lock()
c.messageQueue <- message
c.mu.Unlock()
}
}
} func Send(user string, returnMessage []byte, logger logx.Logger) {
manager.mu.RLock()
client, exists := manager.clients[user]
manager.mu.RUnlock()
if !exists {
logger.Infof("client not found for user:%s message:%s", user, string(returnMessage))
return
}
client.mu.Lock()
err := client.conn.WriteMessage(websocket.TextMessage, returnMessage)
client.mu.Unlock()
if err != nil {
logger.Errorf("client.conn.WriteMessage error %s", err.Error())
// 主动从 manager 中移除无效连接
manager.mu.Lock()
delete(manager.clients, user)
manager.mu.Unlock()
_ = client.conn.Close()
}
} type ClientManager struct {
clients map[string]*Client
mu sync.RWMutex
} var manager = ClientManager{
clients: make(map[string]*Client),
} func ChatWebsocketHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
logger := logx.WithContext(r.Context())
if err != nil {
logger.Errorf("upgrade:%+v", err)
return
}
// 假设前端会发送一个用户 ID 或会话 ID 用于识别连接
user := r.URL.Query().Get("user")
if user == "" {
logger.Errorf("user is empty:")
_ = conn.Close()
return
}
client := NewClient(user, conn)
// 将新的连接存储到连接管理器中
manager.mu.Lock()
oldClient, exists := manager.clients[user]
if exists {
// 关闭旧的连接
_ = oldClient.conn.Close()
}
manager.clients[user] = client
manager.mu.Unlock() go client.ReadPump() l := chat.NewChatWebsocketLogic(r.Context(), svcCtx) for {
select {
case message := <-client.messageQueue:
var req types.ChatWebsocketRequest
_ = json.Unmarshal(message, &req) if req.Heartbeat {
// 处理心跳消息
err = client.conn.WriteMessage(websocket.PongMessage, []byte(""))
if err != nil {
logger.Errorf("write pong message failed:", err)
manager.mu.Lock()
delete(manager.clients, user)
manager.mu.Unlock()
_ = client.conn.Close()
return
}
returnMessage, _ := json.Marshal(true)
Send(user, returnMessage, logger)
continue
} channel := make(chan string, 50) go func() {
defer func() {
close(channel)
close(baseInfoCh)
}()
res := &types.ChatWebsocketResponse{}
res, errChat := l.ChatWebsocket(&req, channel, baseInfoCh)
if errChat != nil {
logger.Error("ChatWebsocketHandler error :", errChat)
res.ErrorMessage = response.GetErrorMessage(errChat)
} returnMessage, _ := json.Marshal(res)
Send(user, returnMessage, logger)
}() var rs []rune
length := 1
for {
s, ok := <-channel
if !ok {
if len(rs) > 0 {
SendSocketMessage(string(rs), req.MessageId)
rs = []rune{}
}
break
}
rs = append(rs, []rune(s)...) if len(rs) > length {
SendSocketMessage(string(rs), req.MessageId)
rs = []rune{}
if length < 4 {
length++
}
}
}
}
}
}
} func SendSocketMessage(message, messageId string) {
baseReturn := &types.ChatWebsocketResponse{}
returnMessage, _ := json.Marshal(types.ChatWebsocketResponse{
Message: message,
MessageId: messageId,
})
Send(user, returnMessage, logger)
}

go 使用websocket的更多相关文章

  1. 漫扯:从polling到Websocket

    Http被设计成了一个单向的通信的协议,即客户端发起一个request,然后服务器回应一个response.这让服务器很为恼火:我特么才是老大,我居然不能给小弟发消息... 轮询 老大发火了,小弟们自 ...

  2. 细说WebSocket - Node篇

    在上一篇提高到了 web 通信的各种方式,包括 轮询.长连接 以及各种 HTML5 中提到的手段.本文将详细描述 WebSocket协议 在 web通讯 中的实现. 一.WebSocket 协议 1. ...

  3. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  4. WebSocket - ( 一.概述 )

    说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...

  5. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  6. Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!

    随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...

  7. Cowboy 开源 WebSocket 网络库

    Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...

  8. 借助Nodejs探究WebSocket

    文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...

  9. 细说websocket - php篇

    下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket ...

  10. webSocket and LKDBHelper的使用说明

    socketket与lkdbhelper来处理数据 客户需求: 当我们有需要从自己的后台推送消息给我们的用户时,用户需要实时的接收到来自我们的推送消息.前提是没有使用第三方的推送框架,那么这个使用we ...

随机推荐

  1. 在Linux中清理Buff/cache

    在 Linux 中,缓冲区和缓存是为提高系统性能而保留的,但如果这些缓存过多,可能会消耗大量内存,影响系统的性能.有时候,您可能需要手动清理这些缓存以释放内存.但请注意,通常不建议定期或频繁地这样做, ...

  2. 【MySQL】Navicat踩坑:Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_general_ci,IMPLICIT) for operation 'instr'

    在Navicat客户端上面执行SQL报错 SQL语句: WITH RECURSIVE transfer (start_station, stop_station, stops, path) AS ( ...

  3. 【Server - 运维】更改腾讯云数据库参数设置

    购买的MySQL实例是一个屏蔽了后台设置的服务器: 默认大小写设置是使用严格区分的: 要设置忽略大小写,就要在my.cnf中更改配置参数 https://cloud.tencent.com/devel ...

  4. P6764 [APIO2020] 粉刷墙壁

    思路: 本质上能进行的操作就是我们算出从第 \(i\) 块砖开始,连续刷 \(M\) 块砖,是否有承包商可以刷出期望颜色. 那么设 \(f_i\) 表示 \([i,i+m-1]\) 是否合法,那么就变 ...

  5. php日常收获

    php 1.sprintf 用法(晚上写成blog w3cschool可查) 2.使用thinkphp getfield 方法时只查询一个字段默认返回第一条数据, 如果想要返回数组需要写成: $thi ...

  6. Kotlin 面向对象编程 (OOP) 基础:类、对象与继承详解

    什么是面向对象编程 (OOP)? OOP 代表面向对象编程. 过程式编程是编写执行数据操作的过程或方法,而面向对象编程则是创建包含数据和方法的对象. 与过程式编程相比,面向对象编程具有以下几个优势: ...

  7. TGI 多-LoRA: 部署一次,搞定 30 个模型的推理服务

    你是否已厌倦管理多个 AI 模型所带来的复杂性和高成本? 那么, 如果你可以部署一次就搞定 30 个模型推理服务会如何? 在当今的 ML 世界中,哪些希望充分发挥其数据的价值的组织可能最终会进入一个& ...

  8. spring手动事务控制

    在项目开发中需要用到手动事务进行控制.现说下遇到的问题以及解决方案: 如果程序需要使用嵌套事务,则需要在配置文件中添加一个配置属性,如下: <bean id="transactionM ...

  9. spring声明事务失效问题

    问题:      在项目开发中遇到了一个spring事务失效的问题,检查配置文档,都没有问题,其他的类中的方法都能进行事务管理,而这个类中的方法却不行. 分析      查看代码发现三个问题: 原因1 ...

  10. Win32 SDK(四)Edit控件用法

    Win32 SDK(四)Edit控件用法 1获得控件句柄 HWND hEdit2 = ::GetDlgItem(hWnd, IDC_EDIT2); WINUSERAPI HWND WINAPI Get ...