使用 Go 语言实现简单聊天系统
在互联网时代,聊天系统是常见的应用场景之一。无论是即时通讯、在线客服还是多人游戏中的消息系统,聊天功能的实现都是必不可少的。本文将使用 Go 语言,结合 WebSocket 来构建一个简单的多人聊天室系统。
一、项目结构
首先,我们设计一个简单的项目结构,文件结构如下:
go-chat/
│
├── main.go // 主程序
├── client.go // 处理客户端连接
└── hub.go // 消息管理
WebSocket 简介
WebSocket 是一种基于 TCP 的网络协议,允许客户端和服务端建立持久的全双工通信连接。相比于传统的 HTTP 请求-响应模型,WebSocket 更加适合实时通信场景,因此它是实现聊天系统的理想选择。
二、实现思路
- 客户端连接管理:每个客户端通过 WebSocket 连接到服务器,服务器会为每个连接的客户端分配一个唯一的
connection。 - 消息广播:当某个客户端发送消息时,服务器将该消息广播给所有连接的客户端。
- 并发处理:Go 原生支持并发编程,通过 Goroutine 和 Channel 可以轻松处理并发消息传递。
三、详细实现
1. main.go - 启动 WebSocket 服务
package mainimport ("log""net/http"
)func main() {hub := newHub()go hub.run()http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {serveWs(hub, w, r)})log.Println("服务器启动,监听端口 8080...")err := http.ListenAndServe(":8080", nil)if err != nil {log.Fatal("监听失败:", err)}
}
main.go 文件的作用是启动 HTTP 服务器,并在 /ws 路径上处理 WebSocket 连接请求。
2. hub.go - 消息管理中心
Hub 负责管理所有的客户端连接,以及消息的广播。
package main// Hub 负责管理所有客户端的注册、注销及消息广播
type Hub struct {clients map[*Client]bool // 已连接的客户端broadcast chan []byte // 从客户端接收的广播消息register chan *Client // 注册请求unregister chan *Client // 注销请求
}func newHub() *Hub {return &Hub{clients: make(map[*Client]bool),broadcast: make(chan []byte),register: make(chan *Client),unregister: make(chan *Client),}
}func (h *Hub) run() {for {select {case client := <-h.register:h.clients[client] = truecase client := <-h.unregister:if _, ok := h.clients[client]; ok {delete(h.clients, client)close(client.send)}case message := <-h.broadcast:for client := range h.clients {select {case client.send <- message:default:close(client.send)delete(h.clients, client)}}}}
}
- clients:保存当前连接的所有客户端。
- broadcast:一个通道,用于广播消息给所有客户端。
- register/unregister:用于客户端连接和断开的注册和注销。
3. client.go - 处理客户端连接
每个客户端的连接由 Client 结构体表示,并包含了 WebSocket 连接和发送消息的通道。
package mainimport ("github.com/gorilla/websocket""log""net/http""time"
)const (writeWait = 10 * time.SecondpongWait = 60 * time.SecondpingPeriod = (pongWait * 9) / 10
)var upgrader = websocket.Upgrader{ReadBufferSize: 1024,WriteBufferSize: 1024,
}type Client struct {hub *Hubconn *websocket.Connsend chan []byte
}func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {conn, err := upgrader.Upgrade(w, r, nil)if err != nil {log.Println("升级到 WebSocket 失败:", err)return}client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}client.hub.register <- clientgo client.writePump()go client.readPump()
}func (c *Client) readPump() {defer func() {c.hub.unregister <- cc.conn.Close()}()c.conn.SetReadLimit(512)c.conn.SetReadDeadline(time.Now().Add(pongWait))c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })for {_, message, err := c.conn.ReadMessage()if err != nil {if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {log.Printf("读取错误: %v", err)}break}c.hub.broadcast <- message}
}func (c *Client) writePump() {ticker := time.NewTicker(pingPeriod)defer func() {ticker.Stop()c.conn.Close()}()for {select {case message, ok := <-c.send:c.conn.SetWriteDeadline(time.Now().Add(writeWait))if !ok {c.conn.WriteMessage(websocket.CloseMessage, []byte{})return}w, err := c.conn.NextWriter(websocket.TextMessage)if err != nil {return}w.Write(message)n := len(c.send)for i := 0; i < n; i++ {w.Write(<-c.send)}if err := w.Close(); err != nil {return}case <-ticker.C:c.conn.SetWriteDeadline(time.Now().Add(writeWait))if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {return}}}
}
serveWs:处理每个 WebSocket 连接请求,并为每个连接创建一个客户端实例。readPump:从 WebSocket 连接中读取消息,并将消息广播到所有客户端。writePump:负责将消息发送给客户端,并定期发送心跳检测(ping)消息以保持连接。
四、运行项目
-
首先,安装 WebSocket 依赖:
go get github.com/gorilla/websocket -
编写前端 HTML 页面(可用于测试),例如
index.html:<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Go 聊天室</title> </head> <body><div id="chatbox"></div><input id="msg" type="text" /><button onclick="sendMessage()">发送</button><script>var ws = new WebSocket("ws://localhost:8080/ws");ws.onmessage = function(event) {var chatbox = document.getElementById('chatbox');chatbox.innerHTML += event.data + "<br/>";};function sendMessage() {var msg = document.getElementById("msg").value;ws.send(msg);}</script> </body> </html> -
运行 Go 服务:
go run main.go -
打开浏览器,访问
index.html,即可体验多人聊天室的功能。
相关文章:
使用 Go 语言实现简单聊天系统
在互联网时代,聊天系统是常见的应用场景之一。无论是即时通讯、在线客服还是多人游戏中的消息系统,聊天功能的实现都是必不可少的。本文将使用 Go 语言,结合 WebSocket 来构建一个简单的多人聊天室系统。 一、项目结构 首先,我们…...
用友U8二次开发工具KK-FULL-*****-EFWeb使用方法
1、安装: 下一步,下一步即可。弹出黑框不要关闭,让其自动执行并关闭。 2、服务配置: 输入服务器IP地址,选择U8数据源,输入U8用户名及账号,U8登录日期勾选系统日期。测试参数有效性,提示测试通过…...
【经验帖】脏读和不可重复读的概念及影响
脏读和不可重复读是数据库事务并发执行时可能出现的两种数据一致性问题,它们对数据的一致性和完整性有着显著的影响。以下是脏读和不可重复读的具体影响: 脏读的影响 脏读发生在一个事务读取了另一个事务未提交的数据时。由于这些数据尚未被提交&#x…...
MTK zephyr平台:USB升级、枚举流程
一、USB升级流程 通过代码及log分析,当前平台升级过程在PL阶段进行 USB download相关代码 mtk/modules/hal/boot/preloader/platform/flashc/ mtk/modules/hal/boot/preloader/platform/board_name/flash/ mtk/modules/hal/boot/preloader/platform/board_name/src/drive…...
golang操作mysql利器-gorm
1、傻瓜示例 GORM通过将数据库表中的数据映射到面向对象的模型中,简化了数据库操作,使得开发者可以很方便的使用代码来操作数据库,而无需编写SQL语句。 目前有个mysql表:miniprogram_orders,其存储了所有用户对应的订…...
09 Shell Scriptfor循环结构语句
Shell Scriptfor循环结构语句 一、Shell FOR循环语句概述 属于shell的符合语句 可以看出帮助信息给出了两种语法 [rootlocalhost ~]# help for for: for NAME [in WORDS ... ] ; do COMMANDS; doneExecute commands for each member in a list.The for loop executes…...
【Java】并发集合
并发集合(java.util.concurrent) 一、List CopyOnWriteArrayList(ReentrantLock实现线程安全) (1)并发修改(写操作)时保证线程安全: 通过ReentrantLock实现多个线程并…...
活动邀请|景联文科技与您相约华为全联接大会2024
2024年9月19-21日,第九届华为全联接大会(简称:HUAWEICONNECT2024)将在上海世博展览馆和上海世博中心举办。 作为华为的旗舰盛会,本次大会以“共赢行业智能化”为主题将邀请思想领袖、商业精英、技术专家、合作伙伴、开发者等业界同仁…...
周边游|基于springBoot的周边游平台设计与实现(附项目源码+论文+数据库)
私信或留言即免费送开题报告和任务书(可指定任意题目) 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 在如今社会上,关于信息上面的处理,没有任…...
【编程基础知识】mysql是怎样执行一条sql语句的,涉及到哪些环节步骤是,mysql的整体体系结构是啥样的,有哪些组件
一、步骤 MySQL执行一条SQL语句的过程涉及多个环节和步骤。以下是这一过程的概述: 客户端连接:客户端通过连接器(Connector)向MySQL服务器发起连接请求。身份验证:连接器对用户身份进行验证,确保用户有权…...
如何上传tauri项目到csdn gitcode
如何上传tauri项目到csdn gitcode 首先保证项目目录有.gitignore,避免不必要的文件上传分享。 gitignore文件 # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log*node_modules dist dist-ssr *.local# Editor …...
【速成Redis】02 Redis 五大基本数据类型常用命令
前言: 上一节课,我们对redis进行了初步了解,和安装好了redis。【速成Redis】01 Redis简介及windows上如何安装redishttps://blog.csdn.net/weixin_71246590/article/details/142319358?spm1001.2014.3001.5501 该篇博客,我们正…...
UnLua扩展C++函数和蓝图自定义事件
一、通过BlueprintImplementableEvent标记扩展C函数 1、 这个标记表示C不需要实现,让蓝图/Lua重写。 2、首先在C中将LuaImp函数标记为BlueprintImplementableEvent,不需要实现,然后再GetIndex中调用该函数。 MyBaseActor.h UFUNCTION(Bluepr…...
干耳屎硬掏不出来怎么办?质量最好的可视挖耳勺推荐
很多干耳的小伙伴都会用普通耳勺来掏耳朵。由于普通耳勺由于其盲操作的特性,对于耳道非直线结构的清理存在诸多不便。所以市面上出现了可视挖耳勺,让我们清晰的看到自己耳道,更加安全的清洁耳朵。,可视挖耳勺这款产品在市场上越来…...
谷歌 Chrome 最新版升级:更强的安全检查功能守护你的上网安全
谷歌 Chrome 浏览器产品经理 Andrew Kamau 在最新发布的博文中宣布,Chrome 浏览器迎来了新一轮的安全升级。新版 Chrome 在后台自动运行安全检查功能,采取了额外的主动措施来保障用户的安全。 自动撤销通知权限 新版 Chrome 浏览器采用了一项基于谷歌安…...
深度学习自编码器 - 收缩自编码器(CAE)篇
序言 在深度学习的浪潮中,收缩自编码器( Compressive Autoencoder, CAE \text{Compressive Autoencoder, CAE} Compressive Autoencoder, CAE)作为自编码器的一种高级形式,正逐步崭露头角。收缩自编码器在保留自编码器核心功能—…...
Dubbo与SpringCloud的区别和优缺点
经常会有同学问我,Dubbo和SpringCloud的选择。甚至也经常会有面试官就这个问题刨根问底。 说实话,其实我不太喜欢回答这个问题,本质上来讲,Dubbo的SpringCloud可以算是完全不同赛道的两种东西,就好像问大家西瓜和土豆我…...
★ C++进阶篇 ★ 多态
Ciallo~(∠・ω< )⌒☆ ~ 今天,我将继续和大家一起学习C进阶篇第二章----多态 ~ ❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️ 澄岚主页:椎名澄嵐-CSDN博客 C基础篇专栏:★ C基础篇 ★_椎名澄嵐的博客-CSDN博客 …...
pg入门3—详解tablespaces2
pg默认的tablespace的location为空,那么如果表设置了默认的tablespace,数据实际上是存哪个目录的呢? 在 PostgreSQL 中,如果你创建了一个表并且没有显式指定表空间(tablespace),或者表空间的 location 为…...
python 爬虫 selenium 笔记
todo 阅读并熟悉 Xpath, 这个与 Selenium 密切相关、 selenium selenium 加入无图模式,速度快很多。 from selenium import webdriver from selenium.webdriver.chrome.options import Options# selenium 无图模式,速度快很多。 option Options() o…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
