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

使用 Go 语言实现简单聊天系统

在互联网时代,聊天系统是常见的应用场景之一。无论是即时通讯、在线客服还是多人游戏中的消息系统,聊天功能的实现都是必不可少的。本文将使用 Go 语言,结合 WebSocket 来构建一个简单的多人聊天室系统。

一、项目结构

首先,我们设计一个简单的项目结构,文件结构如下:

go-chat/
│
├── main.go          // 主程序
├── client.go        // 处理客户端连接
└── hub.go           // 消息管理

WebSocket 简介

WebSocket 是一种基于 TCP 的网络协议,允许客户端和服务端建立持久的全双工通信连接。相比于传统的 HTTP 请求-响应模型,WebSocket 更加适合实时通信场景,因此它是实现聊天系统的理想选择。

二、实现思路

  1. 客户端连接管理:每个客户端通过 WebSocket 连接到服务器,服务器会为每个连接的客户端分配一个唯一的 connection
  2. 消息广播:当某个客户端发送消息时,服务器将该消息广播给所有连接的客户端。
  3. 并发处理: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)消息以保持连接。

四、运行项目

  1. 首先,安装 WebSocket 依赖:

    go get github.com/gorilla/websocket
    
  2. 编写前端 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>
    
  3. 运行 Go 服务:

    go run main.go
    
  4. 打开浏览器,访问 index.html,即可体验多人聊天室的功能。

http://www.lryc.cn/news/441776.html

相关文章:

  • 用友U8二次开发工具KK-FULL-*****-EFWeb使用方法
  • 【经验帖】脏读和不可重复读的概念及影响
  • MTK zephyr平台:USB升级、枚举流程
  • golang操作mysql利器-gorm
  • 09 Shell Scriptfor循环结构语句
  • 【Java】并发集合
  • 活动邀请|景联文科技与您相约华为全联接大会2024
  • 周边游|基于springBoot的周边游平台设计与实现(附项目源码+论文+数据库)
  • 【编程基础知识】mysql是怎样执行一条sql语句的,涉及到哪些环节步骤是,mysql的整体体系结构是啥样的,有哪些组件
  • 如何上传tauri项目到csdn gitcode
  • 【速成Redis】02 Redis 五大基本数据类型常用命令
  • UnLua扩展C++函数和蓝图自定义事件
  • 干耳屎硬掏不出来怎么办?质量最好的可视挖耳勺推荐
  • 谷歌 Chrome 最新版升级:更强的安全检查功能守护你的上网安全
  • 深度学习自编码器 - 收缩自编码器(CAE)篇
  • Dubbo与SpringCloud的区别和优缺点
  • ★ C++进阶篇 ★ 多态
  • pg入门3—详解tablespaces2
  • python 爬虫 selenium 笔记
  • git分支管理的一些常用规范
  • GPT-4论文阅读
  • this 指向
  • 【贪心算法】贪心算法一
  • windnd.hook_dropfiles中的create_buffer值太小无法拖放长文件名
  • Gitlab runner的使用示例(二):Maven + Docker 自动化构建与部署
  • QNX Hypervisor(十)Linux Guest IPC 二
  • 怎样把PPT上顽固的图标删了
  • 【论文阅读】Slim Fly: A Cost Effective Low-Diameter Network Topology 一种经济高效的小直径网络拓扑
  • Prometheus使用Pushgateway推送数据
  • 【Oracle】调优与oracle最大连接数配置