這個demo實現了:git
經過命令行來進行聊天github
具體邏輯都在 websocket.go 這個文件裏web
這裏的核心就是 aliveList
這個全局變量, 負責把消息分發給各客戶端, 事件用channel來傳遞, 減小阻塞json
單個連接會在 aliveList
中註冊, ConnList 就是全部活躍的連接websocket
// 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 }
服務啓動後會執行事件監聽循環socket
// 啓動監聽 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 等類型就是這裏了 }
若是有空閒時間就再搞搞多聊天室的實現, 以及優化一下目前的事件循環邏輯若是還有更多的餘力, 就搞一個好看點的客戶端?命令行