這個demo實現了:
- 消息廣播
- 心跳檢測
通過命令行來進行聊天
具體邏輯都在 websocket.go 這個文件裏
這裏的核心就是 aliveList
這個全局變量, 負責把消息分發給各客戶端, 事件用channel來傳遞, 減少阻塞
單個鏈接會在 aliveList
中註冊, ConnList 就是所有活躍的鏈接
// AliveList 當前在線列表
type AliveList struct {
ConnList map[string]*Client
register chan *Client
destroy chan *Client
broadcast chan Message
cancel chan int
Len int
}
// Client socket客戶端
type Client struct {
ID string
conn *websocket.Conn
cancel chan int
}
服務啓動後會執行事件監聽循環
// 啓動監聽
func (al *AliveList) run() {
log.Println("開始監聽註冊事件")
for {
select {
case client := <-al.register:
log.Println("註冊事件:", client.ID)
al.ConnList[client.ID] = client
al.Len++
al.SysBroadcast(ConnectedMessage, Message{
ID: client.ID,
Content: "connected",
SentAt: time.Now().Unix(),
})
case client := <-al.destroy:
log.Println("銷燬事件:", client.ID)
err := client.conn.Close()
if err != nil {
log.Printf("destroy Error: %v \n", err)
}
delete(al.ConnList, client.ID)
al.Len--
case message := <-al.broadcast:
log.Printf("廣播事件: %s %s %d \n", message.ID, message.Content, message.Type)
for id := range al.ConnList {
if id != message.ID {
err := al.sendMessage(id, message)
if err != nil {
log.Println("broadcastError: ", err)
}
}
}
case sign := <-al.cancel:
log.Println("終止事件: ", sign)
os.Exit(0)
}
}
}
因爲消息的類型比較多, 單純字符串無法滿足需求, 就選用了比較常用的json格式去傳遞, 消息目前分:
const (
// SystemMessage 系統消息
SystemMessage = iota
// BroadcastMessage 廣播消息(正常的消息)
BroadcastMessage
// HeartBeatMessage 心跳消息
HeartBeatMessage
// ConnectedMessage 上線通知
ConnectedMessage
// DisconnectedMessage 下線通知
DisconnectedMessage
)
// Message 消息體結構
type Message struct {
ID string
Content string
SentAt int64
Type int // <- SystemMessage 等類型就是這裏了
}
如果有空閒時間就再搞搞多聊天室的實現, 以及優化一下目前的事件循環邏輯如果還有更多的餘力, 就搞一個好看點的客戶端?