在前面2篇的文章,我们一直说的是一个客户端和一个服务的端的交互模式,现在我们来说下群聊模式,即多个客户端和一个服务端的交互模式,关键的知识点在于服务端如何转发消息。
我们现在通过一个map来存储所有的连接,然后通过遍历map来对所有连接发消息,代码如下:
package main /* * 群聊--广播消息 */ import ( "bufio" "fmt" "math/rand" "net" "strings" "sync" "time" ) //保存连接 var connList sync.Map //随机字符串 func randName() string { var letterRunes = []rune("123456789") rand.Seed(time.Now().UnixNano()) b := make([]rune, 6) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) } func DealConn(conn net.Conn) { // 处理完关闭连接 defer conn.Close() fmt.Println("new conn\n") name := randName() connList.Store(name, conn) conn.Write([]byte("you is : " + name + "\r\n")) // 针对当前连接做发送和接受操作 for { reader := bufio.NewReader(conn) // 读取字符串, 直到碰到回车返回 str, err := reader.ReadString('\n') // 数据读取正确 if err == nil { // 去掉字符串尾部的回车 str = strings.TrimSpace(str) //广播消息 connList.Range(func(k, userConn interface{}) bool { userConn.(net.Conn).Write([]byte(" say : " + str + "\r\n")) return true }) } else { //删除保存的连接 connList.Delete(name) fmt.Println("conn close\n") break } } } func main() { // 建立tcp 服务 listen, err := net.Listen("tcp", "0.0.0.0:1215") if err != nil { fmt.Printf("listen failed, err:%v\n", err) return } for { // 等待客户端建立连接 fmt.Printf("accept conn ...... \n") conn, err := listen.Accept() if err != nil { fmt.Printf("accept failed, err:%v\n", err) continue } // 启动一个单独的 goroutine 去处理连接 go DealConn(conn) } }
启动上面的代码做服务端,然后通过启动多个telnet,就可以发现,每个telnet端发的消息,其他telnet也可以收到.
一般群的相关信息都维护在DB中,发群消息的时候通过群ID来获取群成员,然后再发给群成员