go 使用websocket
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的更多相关文章
- 漫扯:从polling到Websocket
Http被设计成了一个单向的通信的协议,即客户端发起一个request,然后服务器回应一个response.这让服务器很为恼火:我特么才是老大,我居然不能给小弟发消息... 轮询 老大发火了,小弟们自 ...
- 细说WebSocket - Node篇
在上一篇提高到了 web 通信的各种方式,包括 轮询.长连接 以及各种 HTML5 中提到的手段.本文将详细描述 WebSocket协议 在 web通讯 中的实现. 一.WebSocket 协议 1. ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- WebSocket - ( 一.概述 )
说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...
- php+websocket搭建简易聊天室实践
1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...
- Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...
- Cowboy 开源 WebSocket 网络库
Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...
- 借助Nodejs探究WebSocket
文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...
- 细说websocket - php篇
下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket ...
- webSocket and LKDBHelper的使用说明
socketket与lkdbhelper来处理数据 客户需求: 当我们有需要从自己的后台推送消息给我们的用户时,用户需要实时的接收到来自我们的推送消息.前提是没有使用第三方的推送框架,那么这个使用we ...
随机推荐
- nginx灰度发布、网站限速和防盗链
一.灰度发布(金丝雀发布) 灰度发布时使用比较平稳的过渡方式升级或者替换产品项目的方法统称 主要作用 及时发现项目问题 尽早获取用户反馈的信息,以改进产品 如果项目产生问题,可以将问题影响控制到最小范 ...
- hbuilderx生成ios证书和上架全教程
现在很多公司都使用uniapp作为底层框架来开发app应用,而uniapp的开发工具hbuilderx云打包的时候,需要证书和证书profile文件. 假如是ios应用,则还需要上架到appstore ...
- 【Java】部门集合树状顺序展示
一.需求效果: 表单的部门下拉选择时,可以展示部门的层级: 按照这个效果展示,但是不是树,还是原来的集合 二.实现方案: 用Java代码实现两个部分 1.展示Label效果处理 2.处理集合的树状排序 ...
- 【CentOS】tar包安装Tomcat
下载Linux版本的Tomcat[Tar包] 上传到Linux 解压Tar包 tar -zxvf apache-tomcat-8.5.55.tar.gz 目录重命名简化名称[可不做] mv apach ...
- 【Hibernate】Re07 关系映射处理
一.单向多对一关系映射处理 演示案例列举了员工与部门的关系,一个部门下具有多个员工,相反的一个员工只隶属于一个部门下面 Maven依赖坐标: <dependency> <groupI ...
- 外文论文同行评审平台——PubPeer——论文打假平台
参考: https://baijiahao.baidu.com/s?id=1757051752090030001&wfr=spider&for=pc ================= ...
- WhaleStudio 分钟级构建 AI 模型,强大 Ops 能力简化模型调度与部署
什么是机器学习(ML)? 它有什么作用 机器学习(ML)是人工智能(AI)的一个子集,通过算法发现数据中的通用模式,并根据持续不断的训练来优化调整最终结果.ML模型从过去的经验中学习,并根据已有的经验 ...
- SMU Summer 2023 Contest Round 3
SMU Summer 2023 Contest Round 3 A. Curriculum Vitae 题意就是要求\(1\)后面不能有\(0\)的情况下的子序列最长长度, 也就是求一个最长不下降子序 ...
- 从0实现基于Linux socket聊天室-实现聊天室的登录、注册功能-3
上一篇我们已经讲了如何搭建一个多线程的服务器模型,可以支持多个客户端同时连接服务器,本篇我们来实现多个客户端,如何实现向服务器注册信息,并实现登录的功能. 数据结构 接着上一篇的实例代码继续增加功能. ...
- 使用 preloadComponents 进行组件预加载
title: 使用 preloadComponents 进行组件预加载 date: 2024/8/18 updated: 2024/8/18 author: cmdragon excerpt: 摘要: ...