Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go语言聊天系统

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

作者:小于负无穷

本文介绍了如何使用Go语言和WebSocket技术构建一个简单的多人聊天室系统,包括客户端连接管理、消息广播和并发处理,最后,通过编写main.go、hub.go和client.go等核心代码模块,具有一定的参考价值,感兴趣的可以了解一下

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

一、项目结构

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

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

WebSocket 简介

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

二、实现思路

三、详细实现

1. main.go - 启动 WebSocket 服务

package main

import (
    "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] = true
        case 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)
                }
            }
        }
    }
}

3. client.go - 处理客户端连接

每个客户端的连接由 Client 结构体表示,并包含了 WebSocket 连接和发送消息的通道。

package main

import (
    "github.com/gorilla/websocket"
    "log"
    "net/http"
    "time"
)

const (
    writeWait = 10 * time.Second
    pongWait  = 60 * time.Second
    pingPeriod = (pongWait * 9) / 10
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

type Client struct {
    hub  *Hub
    conn *websocket.Conn
    send 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 <- client

    go client.writePump()
    go client.readPump()
}

func (c *Client) readPump() {
    defer func() {
        c.hub.unregister <- c
        c.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
            }
        }
    }
}

四、运行项目

首先,安装 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语言聊天系统内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文