在前面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来获取群成员,然后再发给群成员