Go Academy · 课时

广播消息

向多个客户端发送消息

第 3 / 4 课13 个步骤

广播消息 是 CoddyKit 上的免费 Go Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。

向多个客户端发送消息

聊天室和实时信息流需要将一条消息推送给每个已连接的客户端。由于每个 WebSocket 只能有一个写入器,因此您需要一种协调模式:中心。

中心模式

中心是一个负责管理客户端集合和广播通道的中央协程。客户端进行注册和注销,中心将消息分发给所有客户端。没有多个协程会直接访问共享映射。

中心数据结构

中心包含:

  • 一个客户端集合(map[*Client]bool)
  • 一个传递消息的 broadcast 通道
  • register 和 unregister 通道
type Hub struct {
    clients    map[*Client]bool
    broadcast  chan []byte
    register   chan *Client
    unregister chan *Client
}

中心运行循环

单个 Run 协程通过 select 处理各个通道。由于只有这个协程会修改映射,因此不需要互斥锁。

func (h *Hub) Run() {
    for {
        select {
        case c := <-h.register:
            h.clients[c] = true
        case c := <-h.unregister:
            delete(h.clients, c)
            close(c.send)
        case msg := <-h.broadcast:
            for c := range h.clients {
                c.send <- msg
            }
        }
    }
}

每个客户端的发送通道

每个客户端都有一个带缓冲的 send chan []byte。中心向其中写入消息,专用写入协程从中取出消息并写入套接字。这遵守了一个写入器的规则。

type Client struct {
    conn *websocket.Conn
    send chan []byte
}

写入协程

每个客户端都会运行一个 writePump,从 send 中依次取出消息并写入套接字。如果通道关闭,它就会发送关闭帧。

func (c *Client) writePump() {
    for msg := range c.send {
        c.conn.WriteMessage(websocket.TextMessage, msg)
    }
}

避免慢客户端

如果某个客户端速度很慢,其带缓冲的 send 通道就会被填满。一种常见策略是:缓冲区已满时移除该客户端,而不是阻塞整个中心。

select {
case c.send <- msg:
default:
    close(c.send)
    delete(h.clients, c)
}

为什么使用通道而不是互斥锁

Go 的格言是:“通过通信来共享内存。”通过通道让一个协程统一处理所有修改,可以避免客户端映射上的竞态条件,而无需显式加锁。

广播消息

任何处理函数都可以向中心通道发送消息来进行广播;中心会将消息分发给每个已注册的客户端。

hub.broadcast <- []byte("server announcement")

定向发送与广播

对于聊天室或私信,请按房间或用户 ID 保存客户端,并且只遍历相关子集,而不是所有客户端。这样即使聊天包含许多房间,也能保持高效。

可运行示例:使用通道进行分发

这个自包含程序仅使用标准库和协程,模拟中心将一条消息分发到多个客户端通道。

package main

import (
    "fmt"
    "sync"
)

func main() {
    clients := []chan string{make(chan string, 1), make(chan string, 1), make(chan string, 1)}
    msg := "hello all"
    for _, c := range clients {
        c <- msg // fan-out
    }
    var wg sync.WaitGroup
    for i, c := range clients {
        wg.Add(1)
        go func(id int, ch chan string) {
            defer wg.Done()
            fmt.Printf("client %d got: %s\n", id, <-ch)
        }(i, c)
    }
    wg.Wait()
}

快速检查

请测试您对广播的理解。

回顾

您学会了向多个客户端广播:

  • 中心协程负责管理客户端集合和广播通道
  • 每个客户端都有一个由写入协程取出消息的带缓冲发送通道
  • 这遵守了每个连接只能有一个写入器的规则
  • 移除慢客户端,使中心保持响应
免费开始

用 AI 导师学习 Go — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「广播消息」课时是免费的吗?

是的 — 「广播消息」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「广播消息」这节课中我会学到什么?

向多个客户端发送消息 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「广播消息」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. WebSocket 基础
  2. 使用 gorilla/websocket
  3. 广播消息
  4. 连接管理
← 返回 Go Academy