Golang聊天服务器实现中的几个常见疑问
Golang聊天服务器实现中的几个常见疑问 我正在阅读《Go编程语言》一书。在第8章中,我无法理解如何向多个已连接的客户端广播消息。以下是相关代码。
package main
import (
"bufio"
"fmt"
"log"
"net"
)
//!+broadcaster
type client chan<- string // 一个出站消息通道
var (
entering = make(chan client)
leaving = make(chan client)
messages = make(chan string) // 所有客户端的入站消息
)
func broadcaster() {
clients := make(map[client]bool) // 所有已连接的客户端
for {
select {
case msg := <-messages:
// 将入站消息广播给所有客户端的出站消息通道。
for cli := range clients {
cli <- msg
}
case cli := <-entering:
clients[cli] = true
case cli := <-leaving:
delete(clients, cli)
close(cli)
}
}
}
//!-broadcaster
//!+handleConn
func handleConn(conn net.Conn) {
ch := make(chan string) // 客户端的出站消息
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "You are " + who
messages <- who + " has arrived"
entering <- ch
input := bufio.NewScanner(conn)
for input.Scan() {
messages <- who + ": " + input.Text()
}
// 注意:忽略 input.Err() 的潜在错误
leaving <- ch
messages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg) // 注意:忽略网络错误
}
}
//!-handleConn
//!+main
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcaster()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
这是让我困惑的代码。
func broadcaster() {
clients := make(map[client]bool) // 所有已连接的客户端
for {
select {
case msg := <-messages:
// 将入站消息广播给所有客户端的出站消息通道。
for cli := range clients {
cli <- msg
}
case cli := <-entering:
clients[cli] = true
case cli := <-leaving:
delete(clients, cli)
close(cli)
}
}
}
代码将广播消息发送到客户端的出站通道 ch,但在已连接的 goroutine(handleConn)中,并没有接收此消息并将其写入网络的代码。然而客户端却能收到广播消息,这是为什么?
./netcat
You are 127.0.0.1:53490 ./netcat
You are 127.0.0.1:53565
127.0.0.1:53565 has arrived
Hi
127.0.0.1:53565: Hi
127.0.0.1:53565: Hi
更多关于Golang聊天服务器实现中的几个常见疑问的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang聊天服务器实现中的几个常见疑问的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在 handleConn 函数中,通过 go clientWriter(conn, ch) 启动了一个独立的 goroutine 来专门处理消息发送。clientWriter 函数会持续从通道 ch 中读取消息,并通过 fmt.Fprintln(conn, msg) 写入网络连接。
广播器 broadcaster 将消息发送到每个客户端的 ch 通道,这些消息会被对应的 clientWriter goroutine 接收并发送给客户端。这就是客户端能收到广播消息的原因。
以下是关键代码的说明:
func handleConn(conn net.Conn) {
ch := make(chan string) // 创建客户端的出站消息通道
go clientWriter(conn, ch) // 启动独立的goroutine处理消息发送
// ... 其他代码
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch { // 持续从通道读取消息
fmt.Fprintln(conn, msg) // 将消息写入网络连接
}
}
func broadcaster() {
clients := make(map[client]bool)
for {
select {
case msg := <-messages:
for cli := range clients {
cli <- msg // 将消息发送到每个客户端的通道
}
// ... 其他case
}
}
}
广播流程:
- 当有消息进入
messages通道时,broadcaster遍历所有客户端通道 - 将消息发送到每个客户端的
ch通道 - 每个客户端的
clientWritergoroutine 从自己的ch通道接收消息 clientWriter将消息写入对应的网络连接
这种设计模式是典型的 Go 并发模式:每个客户端连接有两个 goroutine 协同工作:
handleConn:处理输入(读取客户端消息)clientWriter:处理输出(向客户端发送消息)
两个 goroutine 通过通道 ch 进行通信,实现了并发安全的消息传递。

