架构图如下:
本项目由四个文件组成:
- hub.go
- client.go
- main.go
- home.html
Hub结构体实现:
- 拥有每一个Client的指针
- 一个boardcast管道接收任意Client的消息
- 接收用户注册的管道
- 接收用户注销的管道
type Hub struct{
broadcast chan string //broadcast管道里有数据时把它写入每一个Client的send管道中
clients map[*Client]struct{} //Hub持有每个client的指针
register chan *Client //注册管道
unregister chan *Client //注销管道
}
Hub构造函数:
func NewHub()*Hub{
return &Hub{broadcast: make(chan []byte),
clients: make(map[*Client]struct{}),
register: make(chan *Client), unregister: make(chan *Client)}
}
Client结构体实现:
- 与前端的websocket连接
- hub的指针
- send管道传输信息
- name 字符串保存前端用户的姓名
type Client struct{
hub *Hub
conn *websocket.Conn
send chan []byte
name []byte
}
Hub工作实现:
- 若注册管道有输入则在map中注册
- 若注销管道有输入则在map中删除并将该client的send管道关闭
- 若boardcast管道有输入则对map里的每个client的send管道输入
func (hub *Hub) Run(){
for{
select{
case client := <-hub.register:
hub.clients[client] = struct{}{}
case client := <- hub.unregister:
delete(hub.clients,client)
close(client.send)
case msg := <-hub.broadcast:
for client := range hub.clients{
select{
case client.send <- msg://如果管道不能立即写入数据,就认为该client出故障了
default:
close(client.send)
delete(hub.clients, client)
}
}
}
}
}
Client从websocket读取内容:
- 善后工作:注销client,关闭websocket连接
- connection设置最大读入量和ping pong时间
- 死循环读取前端消息
const (
writeWait = 10 * time.Second //
pongWait = 60 * time.Second // 每60秒向websocket发送一次pong
pingPer