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

Go 即时通讯系统:客户端与服务端 WebSocket 通信交互

客户端和服务端的交互

客户端与服务端建立连接

  • 客户端:客户端通过浏览器或者其他应用程序发起一个 HTTP 请求到服务端的 /socket.io 路径。在请求中会携带用户的 UUID 作为参数(通过 c.Query("user") 获取)。
// router/socket.go
func RunSocket(c *gin.Context) {user := c.Query("user") // 获取用户标识if user == "" {return // 无用户标识则拒绝连接}log.Info("newUser", log.String("newUser", user))// 升级 HTTP 连接为 WebSocket 连接ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)if err != nil {return}// 创建客户端对象client := &server.Client{Name: user,Conn: ws,Send: make(chan []byte),}// 注册客户端到服务器server.MyServer.Register <- client// 启动读写协程go client.Read()go client.Write()
}
  • 服务端:服务端接收到请求后,使用 websocket.Upgrader 将 HTTP 连接升级为 WebSocket 连接。然后创建一个 Client 实例,并将其发送到 ServerRegister 通道。

客户端注册到服务端

// internal/server/server.go
func (s *Server) Start() {for {select {// 处理客户端注册case conn := <-s.Register:log.Info("login", log.String("login", conn.Name))s.Clients[conn.Name] = connmsg := &protocol.Message{From:    "System",To:      conn.Name,Content: "welcome!",}protoMsg, _ := proto.Marshal(msg) // 序列化为字节切片conn.Send <- protoMsg}}
}
  • 客户端:无特定操作,等待服务端响应。
  • 服务端:服务端的 Start 方法会监听 Register 通道,当有新的客户端注册时,将客户端信息保存到 Clients 映射中,并向客户端发送欢迎消息。

客户端发送消息到服务端

  • 客户端:客户端通过 Client 结构体的 Read 方法从 WebSocket 连接读取消息。如果是心跳消息,客户端会发送 Pong 响应;否则,根据配置将消息发送到 Kafka 或者服务端的 Broadcast 通道。
// internal/server/client.go
func (c *Client) Read() {// 消息读取for {c.Conn.PongHandler()_, message, err := c.Conn.ReadMessage()if err != nil {log.Error("client read message error", log.Err(err))MyServer.Ungister <- cc.Conn.Close()break}msg := &protocol.Message{}proto.Unmarshal(message, msg)// 处理心跳消息if msg.Type == constant.HEAT_BEAT {pong := &protocol.Message{Content: constant.PONG,Type:    constant.HEAT_BEAT,}pongBytes, err2 := proto.Marshal(pong)if err2 != nil {log.Error("client marshal message error", log.Err(err))}c.Conn.WriteMessage(websocket.BinaryMessage, pongBytes)} else {MyServer.Broadcast <- message}}
}
  • 服务端:服务端的 Start 方法会监听 Broadcast 通道,当接收到消息时,根据消息的类型(单聊、群聊)将消息转发给相应的客户端。
// internal/server/server.go
func (s *Server) Start() {for {select {// 处理消息广播case message := <-s.Broadcast:msg := &protocol.Message{}proto.Unmarshal(message, msg)if msg.To != "" {// 有指定接收者的消息处理if msg.ContentType >= constant.TEXT && msg.ContentType <= constant.VIDEO {// 一般消息,比如文本消息,视频文件消息等_, exists := s.Clients[msg.From]if exists { // 检查发送者是否在连接列表中saveMessage(msg)}if msg.ContentType == constant.MESSAGE_TYPE_USER {// 单人消息处理s.sendUserMessage(msg)} else if msg.ContentType == constant.MESSAGE_TYPE_GROUP {// 多人消息处理s.sendGroupMessage(msg)} else {// 语音电话,视频电话等,仅支持单人聊天,不支持群聊// 不保存文件,直接进行转发client, ok := s.Clients[msg.To]if ok {client.Send <- message}}} else {// 无指定接收者的广播消息处理for id, conn := range s.Clients {log.Info("allUser", log.String("allUser", id))select {case conn.Send <- message: // 发送消息给客户端,成功继续处理default: // 失败关闭客户端close(conn.Send)delete(s.Clients, conn.Name)}}}}}}
}

服务端发送消息到客户端

  • 服务端:服务端将消息发送到客户端的 Send 通道。
// internal/server/server.go
client.Send <- msgByteclient.Send <- message
  • 客户端:客户端的 Write 方法会监听 Send 通道,当接收到消息时,将消息通过 WebSocket 连接发送给客户端。
// internal/server/client.go
func (c *Client) Write() {defer func() {c.Conn.Close()}()for message := range c.Send {c.Conn.WriteMessage(websocket.BinaryMessage, message)}
}

客户端断开连接

  • 客户端:无特定操作,等待服务端响应。
  • 服务端:服务端的 Start 方法会监听 Ungister 通道,当有客户端断开连接时,将客户端信息从 Clients 映射中删除,并关闭客户端的 Send 通道。
// internal/server/server.go
func (s *Server) Start() {for {// 处理客户端注销case conn := <-s.Ungister:log.Info("loginout", log.String("loginout", conn.Name))if _, ok := s.Clients[conn.Name]; ok {close(conn.Send)delete(s.Clients, conn.Name)}}
}

客户端和服务端通过 WebSocket 连接进行实时通信,通过 RegisterUngisterBroadcast 通道进行客户端的注册、注销和消息广播,通过客户端的 Send 通道进行消息的发送。整个交互过程基于 Go 语言的协程和通道机制,实现了高效、并发的通信。

代码地址:server.go,client.go

言的协程和通道机制,实现了高效、并发的通信。

代码地址:server.go,client.go

相关文章:

Go 即时通讯系统:客户端与服务端 WebSocket 通信交互

客户端和服务端的交互 客户端与服务端建立连接 客户端&#xff1a;客户端通过浏览器或者其他应用程序发起一个 HTTP 请求到服务端的 /socket.io 路径。在请求中会携带用户的 UUID 作为参数&#xff08;通过 c.Query("user") 获取&#xff09;。 // router/socket.…...

2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元

2025年5月AI科技领域周报&#xff08;5.19-5.25&#xff09;&#xff1a;大模型多模态突破 具身智能开启机器人新纪元 目录 2025年5月AI科技领域周报&#xff08;5.19-5.25&#xff09;&#xff1a;大模型多模态突破 具身智能开启机器人新纪元一、本周热点回顾1. 百度发布全球首…...

某航后缀混淆逆向与顶像风控分析

文章目录 1. 写在前面2. 接口分析3. 加密分析4. 风控分析 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致…...

[Protobuf]常见数据类型以及使用注意事项

[Protobuf]常见数据类型以及使用注意事项 水墨不写bug 文章目录 一、基本数据类型1、字段2、字段的修饰规则 二、自定义数据类型1、message类型2、enum类型3、Any类型4、oneof类型5、map类型 三、小工具1.hexdump2.decode 四、注意事项 一、基本数据类型 protobuf 支持多种基础…...

【C/C++】面试基础题目收集

C 软件开发面试中常见的刷题题目通常可分为以下几大类&#xff1a;数据结构与算法、系统编程、面向对象设计、C 语言特性、并发编程等。 &#x1f9e0; 一、数据结构与算法&#xff08;力扣/牛客经典题&#xff09; 掌握 STL 和底层结构实现能力&#xff1a; &#x1f4cc; 数…...

模拟实现线程池(线程数目为定值)和定时器

前言 昨天学习关于定时器的相关知识。今天花时间去模拟实现了一个定时器&#xff0c;同时也去模拟实现了一个线程池(线程数目为定值)。我感觉我收获了很多&#xff0c;对于线程的理解加深了。跟大家分享一下~ 线程池和定时器(这个是主要)的实现 代码 线程池 import java.ut…...

数据结构之队列实验

引言 在计算机科学中&#xff0c;进制转换是基础但重要的操作。例如将一个十进制数转换为二进制或八进制表示时&#xff0c;我们通常使用“短除法”——即不断用目标进制去除当前数&#xff0c;记录余数&#xff0c;直到商为0为止。 这种方法得到的是低位先产生的结果&#x…...

Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构

Java求职者面试题详解&#xff1a;计算机网络、操作系统、设计模式与数据结构 第一轮&#xff1a;基础概念问题 1. 请解释什么是HTTP协议&#xff1f; HTTP&#xff08;HyperText Transfer Protocol&#xff09;是一种用于传输超文本的协议&#xff0c;它定义了客户端和服务…...

每日八股文6.1

每日八股-6.1 Go1.Sync.map的底层实现2.结构体的tag如何获取&#xff1f;3.Go实现单例模式&#xff08;使用sync.Once&#xff09;4.Go实现单例模式&#xff08;不使用sync.Once&#xff09;5.make和new的区别6.Go项目引用包为什么用_以及包的init()函数7.如何判断一个结构体是…...

【Ubuntu】摸鱼技巧之虚拟机环境复制

前言 提示&#xff1a;所有的操作都需要关闭虚拟机 如何快速在其它电脑布置&#xff0c;linux环境&#xff0c;如果我们有一个环境直接拷贝就有时间摸鱼呀。 1.直接复制简单粗暴 不做赘述&#xff0c;如果不会复制&#xff0c;那么请右击鼠标压缩复制 2.克隆虚拟机 2.1 …...

室内VR全景助力房产营销及装修

在当今的地产行业&#xff0c;VR全景已成为不可或缺的应用工具。从地产直播到楼市VR地图&#xff0c;从效果图到水电家装施工记录&#xff0c;整个地产行业的上下游生态中&#xff0c;云VR全景的身影无处不在。本文将探讨VR全景在房产营销及装修领域的应用&#xff0c;并介绍众…...

jenkins集成gitlab实现自动构建

jenkins集成gitlab实现自动构建 前面我们已经部署了Jenkins和gitlab&#xff0c;本文介绍将二者结合使用 项目源码上传至gitee提供公网访问&#xff1a;https://gitee.com/ye-xiao-tian/my-webapp 1、创建一个群组和项目 2、添加ssh密钥 #生成密钥 [rootgitlab ~]# ssh-keyge…...

【C语言练习】070. 编写代码处理C语言中的异常情况

070. 编写代码处理C语言中的异常情况 070. 编写代码处理C语言中的异常情况C语言异常处理的基本思路返回值检查示例errno使用示例setjmp/longjmp示例最佳实践建议1. 使用返回值检查错误2. 使用全局变量记录错误状态3. 使用回调函数或信号处理程序4. 使用`setjmp`和`longjmp`示例…...

Java基本数据类型、抽象类和接口、枚举、时间类、String类全面介绍

JAVA基本数据类型知识总结 基本数据类型&#xff08;Primitive Types&#xff09; 类型占用字节默认值范围示例byte10-128 ~ 127byte a 100;short20-32,768 ~ 32,767short b 2000;int40-2 ~ 2-1int c 100000;long80L-2⁶ ~ 2⁶-1long d 10000000000L;float40.0f~7位小数f…...

Spring Boot微服务架构(八):开发之初就引入APM工具监控

使用 APM&#xff08;Application Performance Management&#xff09;工具监控 Spring Boot 应用&#xff0c;可以帮助开发者实时追踪性能瓶颈、分析调用链路、监控资源使用情况&#xff0c;并快速定位故障。以下是详细的步骤和常用工具的选择指南&#xff1a; ​​一、常用 A…...

大规模真实场景 WiFi 感知基准数据集

一段话总结 本文提出CSI-Bench,首个大规模真实场景WiFi感知基准数据集,覆盖26个室内环境、35名用户、16种商用设备,包含461小时有效数据,支持跌倒检测、呼吸监测、定位、运动源识别等单任务及用户身份、活动、 proximity联合标注的多任务学习。通过标准化评估协议和基线模…...

Python实现HPSO-TVAC优化算法优化支持向量机SVC分类模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在当今数据驱动的时代&#xff0c;支持向量机&#xff08;SVM&#xff09;作为一种经典的机器学习算法&#xff0c;…...

ck-editor5的研究 (3):初步使用 CKEditor5 的事件系统和API

前言 在上一篇文章中—— ck-editor5的研究&#xff08;2&#xff09;&#xff1a;对 CKEditor5 进行设计&#xff0c;并封装成一个可用的 vue 组件 &#xff0c;我已经把 CKEditor5 封装成了一个通用vue组件&#xff0c;并且成功在nuxt中运行&#xff0c;并具备一定的通用性&…...

使用ReactNative加载HarmonyOS Svga动画

这是一款使用ReactNative 加载HarmonyOS Svga动画的播放器插件 三端Svga动画统一使用点击这里 版本:v1.1.5 react-native-ohos-svgaplayer [!TIP] Github 地址 安装与使用 npm npm install react-native-ohos-svgaplayer yarn yarn add react-native-ohos-svgaplayer下面…...

WPS快速排版

论文包括&#xff08;按顺序&#xff09;&#xff1a;封面&#xff08;含题目&#xff09;、摘 要、关键词、Abstract&#xff08;英文摘要&#xff09;、Keywords、目录、正文、参考文献、在读期间发表的学术论文及研究成果&#xff0c;致 谢 题目&#xff08;黑小一加粗&…...

Java实现命令行图书管理系统(附完整源码)

一、项目概述 本文将介绍如何使用Java实现一个基于命令行的图书管理系统。系统支持管理员和普通用户两种角色&#xff0c;提供图书的增删改查、借阅归还等功能。项目采用面向对象设计原则&#xff0c;代码结构清晰&#xff0c;适合Java初学者学习。 二、系统功能架构 graph T…...

使用Docker-NVIDIA-GPU开发配置:解决 Docker NVIDIA 运行时错误方法

问题描述 运行 Docker 命令时,系统提示 docker: Error response from daemon: unknown or invalid runtime name: nvidia,表明 Docker 无法识别 NVIDIA 运行时。这一错误通常出现在使用 --runtime=nvidia 和 --gpus 参数时,意味着 NVIDIA 容器运行时未正确安装或配置。NVID…...

如何更好的理解云计算和云原生?

本文介绍什么是云计算、什么是云原生、怎么理解云相关概念&#xff0c;如有问题&#xff0c;欢迎指正。 一、云计算 定义&#xff1a;云计算是通过互联网&#xff08;即“云”&#xff09;按需提供计算资源&#xff08;如服务器、存储、数据库、网络、软件等&#xff09;的服…...

【数据结构】顺序表和链表详解(上)

前言&#xff1a;上期我们介绍了算法的复杂度&#xff0c;知道的算法的重要性同时也了解到了评判一个算法的好与坏就去看他的复杂度(主要看时间复杂度)&#xff0c;这一期我们就从顺序表和链表开始讲起。 文章目录 一&#xff0c;顺序表1&#xff0c;线性表2&#xff0c;顺序表…...

唯创WT2606B TFT显示灵动方案,重构电子锁人机互动界面,赋能智能门锁全场景交互!

在智能家居的浪潮中&#xff0c;门锁搭载显示屏已成为行业创新的焦点。据行业数据显示&#xff0c;2023年全球智能门锁出货量中&#xff0c;搭载显示屏的型号占比已突破40%&#xff0c;且年复合增长率达25%。而2024年国内智能门锁销量突破2200万套&#xff0c;预计2025年市场规…...

WPF的UI交互基石:数据绑定基础

数据绑定基础 1 Binding的Path属性2 ElementName绑定3 DataContext的作用4 绑定模式&#xff08;Binding Mode&#xff09;5 实用技巧集合1. 默认值处理2. 设计时数据3. 绑定验证4. 多级路径监控 6 常见错误排查 数据绑定是WPF的核心特性之一&#xff0c;它实现了界面&#xff…...

智能穿戴新标杆:SD NAND (贴片式SD卡)与 SOC 如何定义 AI 眼镜未来技术路径

目录 一、SD NAND&#xff1a;智能眼镜的“记忆中枢”突破空间限制的存储革命性能与可靠性的双重保障 二、SOC芯片&#xff1a;AI眼镜的“智慧大脑”从性能到能效的全面跃升多模态交互的底层支撑 三、SD NANDSOC&#xff1a;11&#xff1e;2的协同效应数据流水线的高效协同端侧…...

TCP/IP四层模型

TCP/IP四层模型 TCP/IP四层模型将网络通信分为四个层次&#xff1a; 1. 网络接口层&#xff1a;负责计算机与网络硬件间的数据传输&#xff0c;在物理网络上发送/接收数据帧&#xff08;如以太网、Wi-Fi协议&#xff09;。 2. 互联网层&#xff08;网络层&#xff09;&…...

深入浅出Nacos:微服务架构中的服务发现与配置管理利器

在当今的软件开发领域,随着微服务架构的普及,如何有效地进行服务治理和服务配置管理成为了开发者面临的重要挑战之一。阿里巴巴开源的 Nacos(Dynamic Naming and Configuration Service)应运而生,旨在帮助开发者更轻松地构建云原生应用。本文将详细介绍 Nacos 的核心功能、…...

node_modules包下载不下来

如果项目里面的package-lock.json有resolved &#xff0c;就指向了包的下载来源&#xff0c;如果这个网址挂了&#xff0c;那npm i 就会一直卡着。而且&#xff0c;在终端去修改 npm的镜像是没有用的 解决办法是:把项目里面的 lock文件 .npmrc都删了 然后重新下载就可以了...