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

使用 Go 语言实现 WebSocket的核心逻辑

文章目录

    • WebSocket 简介
    • 时序图
    • 核心逻辑
      • Client 结构与功能
      • 创建新客户端
      • 消息读取逻辑 (ReadPump)
      • 发送消息逻辑 (Send)
      • 客户端管理器 (ClientManager)
      • WebSocket 处理器
      • 处理心跳与长连接
    • 总结

本文将基于 Go 语言,通过使用 gorilla/websocket 库来实现一个简单的聊天应用。该应用具备处理 WebSocket 连接、消息传输、以及用户连接管理等功能。我们将详细展示如何实现这些功能,并剖析背后的核心逻辑与原理。

WebSocket 简介

WebSocket 是一种全双工的通信协议,允许客户端和服务器之间在一个持久连接上进行双向数据传输。与 HTTP 的短连接不同,WebSocket 可以在建立连接后保持连接状态,从而实现实时通信。因此,WebSocket 非常适合用于聊天应用等需要实时数据传输的场景。

时序图

客户端 客户端管理器 WebSocket处理器 请求连接 新客户端加入 确认连接 连接成功 发送消息 消息路由 路由结果 消息送达 loop [消息读取] 发送心跳 响应Pong 客户端 客户端管理器 WebSocket处理器

核心逻辑

在本示例中,我们主要实现了以下几个核心模块:

  1. Client:表示单个 WebSocket 连接的客户端,负责处理消息的收发。
  2. ClientManager:用于管理多个客户端的连接,处理客户端的增加、删除以及消息的路由。
  3. WebSocket 处理逻辑:处理新连接的建立、消息的读取与发送。

Client 结构与功能

type Client struct {conn         *websocket.ConnmessageQueue chan []bytemu           sync.Mutexuser         string
}

Client 结构体用于表示一个 WebSocket 客户端连接。每个客户端包含:

  • conn:当前的 WebSocket 连接。
  • messageQueue:用于存储待发送的消息队列。
  • mu:用于保证并发安全的互斥锁。
  • user:表示客户端的用户标识。

创建新客户端

func NewClient(user string, conn *websocket.Conn) *Client {return &Client{conn:         conn,user:         user,messageQueue: make(chan []byte, 100),}
}

NewClient 函数用于创建新的客户端实例。每个客户端都有一个独立的消息队列,用于存储要发送给客户端的消息。

消息读取逻辑 (ReadPump)

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 <- messagec.mu.Unlock()}}
}

ReadPump 方法用于持续从 WebSocket 连接中读取消息,并将接收到的消息存储到 messageQueue 队列中。该方法通过一个无限循环,不断读取 WebSocket 的消息。当出现错误时,例如客户端断开连接,便会关闭当前连接并将该客户端从客户端管理器中移除。

其中,ReadMessage() 方法用于从 WebSocket 连接中读取消息,返回的 mt 表示消息类型。常见的类型包括文本消息(TextMessage)和 ping 消息(PingMessage)。对于这些消息类型,消息会被推送到 messageQueue 以便后续处理。

发送消息逻辑 (Send)

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.mu.Lock()delete(manager.clients, user)manager.mu.Unlock()_ = client.conn.Close()}
}

Send 函数负责向指定的用户发送消息。首先,它会检查用户是否存在于 ClientManager 中,如果不存在则记录日志并返回。如果用户存在,则通过 WriteMessage() 方法将消息发送给客户端。若发送消息时发生错误,会将该用户从连接管理器中移除,并关闭该 WebSocket 连接。

客户端管理器 (ClientManager)

type ClientManager struct {clients map[string]*Clientmu      sync.RWMutex
}var manager = ClientManager{clients: make(map[string]*Client),
}

ClientManager 用于管理多个客户端的连接,clients 字段是一个存储所有客户端连接的映射,键是用户标识,值是客户端对象。通过读写锁 (sync.RWMutex),确保在并发访问时的线程安全。

WebSocket 处理器

ChatWebsocketHandler 是处理 WebSocket 连接的 HTTP 处理函数。

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}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] = clientmanager.mu.Unlock()go client.ReadPump()// 省略其他消息处理逻辑...}
}
  1. 连接升级:首先使用 upgrader.Upgrade() 将 HTTP 请求升级为 WebSocket 连接。
  2. 用户认证:通过 URL 查询参数获取用户 ID,并创建对应的 Client
  3. 旧连接处理:如果该用户已经有一个旧的 WebSocket 连接,则会关闭旧连接。
  4. 启动消息读取:通过启动 ReadPump() 协程,持续读取该用户的 WebSocket 消息。

处理心跳与长连接

在 WebSocket 通信中,维持长连接的一个常用做法是使用心跳机制。

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}
}

每当接收到心跳消息时,服务器会返回一个 PongMessage,以维持连接的活跃状态。如果发送 PongMessage 失败,服务器会关闭该客户端连接。

总结

本文详细展示了如何使用 Go 语言实现一个 WebSocket 聊天应用的核心逻辑。我们讨论了客户端的创建与管理、消息的收发、以及长连接的维持等关键功能。通过这些核心组件,我们可以轻松地扩展功能,构建复杂的 WebSocket 应用。

WebSocket 实现的关键在于良好的连接管理和消息处理机制,这样可以确保在高并发情况下仍然能维持高效且稳定的实时通信。

关注我

相关文章:

使用 Go 语言实现 WebSocket的核心逻辑

文章目录 WebSocket 简介时序图核心逻辑Client 结构与功能创建新客户端消息读取逻辑 (ReadPump)发送消息逻辑 (Send)客户端管理器 (ClientManager)WebSocket 处理器处理心跳与长连接 总结 本文将基于 Go 语言&#xff0c;通过使用 gorilla/websocket 库来实现一个简单的聊天应用…...

Linux下的杀毒软件介绍

Linux下的杀毒软件介绍 一、Linux杀毒软件的基本概念和作用二、Linux杀毒软件的选择三、Linux杀毒软件推荐四、Linux杀毒软件对应用进程的影响五、结论在当今数字化和网络化的环境中,保护计算机系统的安全至关重要。尽管Linux操作系统因其开源、稳定且相对安全的特性而较少受到…...

JSONP详解

JSONP&#xff08;JSON with Padding&#xff09;是一种非官方的协议&#xff0c;它允许在服务器端集成Script tags返回至客户端&#xff0c;通过JavaScript callback的形式实现跨域访问。以下是对JSONP的详细解释&#xff1a; 一、JSONP的背景与原理 背景&#xff1a; 由于浏…...

Leetcode—1115. 交替打印 FooBar【中等】(多线程)

2024每日刷题&#xff08;180&#xff09; Leetcode—1115. 交替打印 FooBar C实现代码 class FooBar { private:int n;sem_t fooSem;sem_t barSem;public:FooBar(int n) {this->n n;sem_init(&fooSem, 0, 1);sem_init(&barSem, 0, 0);}~FooBar() {sem_destroy(&…...

Visual Studio Code基础:使用debugpy调试python程序

相关阅读 VS codehttps://blog.csdn.net/weixin_45791458/category_12658212.html?spm1001.2014.3001.5482 一、安装调试器插件 在VS code中可以很轻松地调试Python程序&#xff0c;首先需要安装Python调试器插件&#xff0c;如图1所示。 图1 安装调试器插件 Python Debugge…...

超全!一文详解大型语言模型的11种微调方法

导读&#xff1a;大型预训练模型是一种在大规模语料库上预先训练的深度学习模型&#xff0c;它们可以通过在大量无标注数据上进行训练来学习通用语言表示&#xff0c;并在各种下游任务中进行微调和迁移。随着模型参数规模的扩大&#xff0c;微调和推理阶段的资源消耗也在增加。…...

C 主要函数解析

1、fseek 函数 int fseek(FILE *stream, long offset, int fromwhere); 第一个参数stream为文件指针 第二个参数offset为偏移量&#xff0c;正数表示正向偏移&#xff0c;负数表示负向偏移 第三个参数origin设定从文件的哪里开始偏移,可能取值为&#xff1a;SEEK_CUR、 SEE…...

vue3学习:数字时钟遇到的两个问题

在前端开发学习中&#xff0c;用JavaScript脚本写个数字时钟是很常见的案例&#xff0c;也没什么难度。今天有时间&#xff0c;于是就用Vue的方式来实现这个功能。原本以为是件非常容易的事&#xff0c;没想到却卡在两个问题上&#xff0c;一个问题通过别人的博文已经找到答案&…...

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)3.7-3.8

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第三周 目标检测&#xff08;Object detection&#xff09;3.7 非极大值抑制&#xff08;Non-max suppression&#xff09;3.8 Anchor Boxes 第四门课 卷积神经网络&#xff08;Convolutional…...

【Linux】最基本的字符设备驱动

前面我们介绍到怎么编译出内核模块.ko文件&#xff0c;然后还加载了这个驱动模块。但是&#xff0c;那个驱动代码还不完善&#xff0c;驱动写好后怎么在应用层使用也没有介绍。 字符设备抽象 Linux内核中将字符设备抽象成一个具体的数据结构&#xff08;struct cdev&#xff…...

利用 Llama 3.1模型 + Dify开源LLM应用开发平台,在你的Windows环境中搭建一套AI工作流

文章目录 1. 什么是Ollama&#xff1f;2. 什么是Dify&#xff1f;3. 下载Ollama4. 安装Ollama5. Ollama Model library模型库6. 本地部署Llama 3.1模型7. 安装Docker Desktop8. 使用Docker-Compose部署Dify9. 注册Dify账号10. 集成本地部署的 Llama 3.1模型11. 集成智谱AI大模型…...

Docker常用命令分享二

docker的用户组管理过程&#xff1a; 1、sudo : 可以让普通用户临时获得root用户的权限&#xff0c;来新建docker用户组 2、普通用户并没有使用sudo的权限 3、先要让root用户把testing用户加入到sudoers的授权文件中 4、sudoers的文件居然是只读的&#xff0c;先解决这个问…...

【一步步开发AI运动小程序】二十、AI运动小程序如何适配相机全屏模式?

引言 受小程序camera组件预览和抽帧图像不一致的特性影响&#xff0c;一直未全功能支持全屏模式&#xff0c;详见本系列文件第四节小程序如何抽帧&#xff1b;随着插件在云上赛事、健身锻炼、AI体测、AR互动场景的深入应用&#xff0c;各开发者迫切的希望能在全屏模式下应用&am…...

[Java基础] 运算符

[Java基础] 基本数据类型 [Java基础] Java HashMap 的数据结构和底层原理 目录 算术运算符 比较运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 常见面试题 Java语言支持哪些类型的运算符&#xff1f; 请解释逻辑运算符&&和&的区别? 请解释条件运…...

[001-02-018].第05节:数据类型及类型转换

我的后端学习大纲 我的Java学习大纲 1、数据类型介绍&#xff1a; 1.0.计算机存储单位&#xff1a; 1.1.基本数据类型介绍&#xff1a; a.整型&#xff1a;byte、short、int、long 1.整型包括&#xff1a;byte、short、int、long&#xff0c;可如下图方式类比记忆&#xff1…...

Netty基础

Netty基础 一级目录I/O请求基础知识Netty如何实现自己的I/O模型 网络框架的选型 Netty整体架构Netty逻辑处理架构网络通信层事件调度层服务编排层 组件关系梳理Netty源码结构 netty是目前最流行的一款高性能java网络编程框架&#xff0c;广泛使用于中间件、直播、社交、游戏等领…...

602,好友申请二:谁有最多的好友

好友申请二&#xff1a;谁有最多的好友 实现 with tmp as (selectrequester_id idfrom RequestAcceptedunion allselectaccepter_id idfrom RequestAccepted )selectid,count(*) num from tmp group by id order by num desc limit 1;...

【Matlab算法MATLAB实现的音频信号时频分析与可视化(附MATLAB完整代码)

MATLAB实现的音频信号时频分析与可视化 前言正文:时频分析实现原理代码实现代码运行结果图及说明结果图:结果说明:总结前言 音频信号的时频分析是信号处理领域中的一个重要研究方向。它允许我们同时观察信号在时间和频率域的特性,为音频处理、语音识别、音乐分析等应用提供…...

界面耻辱纪念堂--可视元素03

更多的迹象表明&#xff0c;关于在程序里使用新的动态界面元素&#xff0c;微软的态度是不确定的&#xff0c;其中一个是仅仅需要对比一下Office97 里的“Coolbars”和“标准工具条”。Coolbar 按钮直到用户指针通过的时候才成为按钮&#xff08;否则是平的&#xff09;。 工具…...

国产龙芯处理器选择迅为2K1000开发板有资料

硬件配置国产龙芯处理器&#xff0c;双核64位系统&#xff0c;板载2G DDR3内存&#xff0c;流畅运行Busybox、Buildroot、Loognix、QT5.12 系统!接口全板载4路USB HOST、2路千兆以太网、2路UART、2路CAN总线、Mini PCIE、SATA固态盘接口、4G接口、GPS接口WIF1、蓝牙、Mini HDMI…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...