搭建程序主体(二)
1. 定义变量
// 定义一个结构体,保存客户端信息
type Client struct {
C chan string
Name string
Addr string
}
// 定义message管道,负责传递用户消息
var message = make(chan string)
// 定义一个map,保存客户端IP和客户端
var onlineMap map[string]Client
2. 主协程实现
func main() {
fmt.Println("服务器启动了...")
// 启动监听器,监听8888端口
lntr, err := net.Listen("tcp", "127.0.0.1:8888")
if (err != nil) {
fmt.Println("net.Listen err: ", err)
return
}
// 关闭监听器
defer lntr.Close()
// 启动Manager协程,负责监听Message通道数据变化
go Manager()
// 循环监听客户端连接
for {
conn, err := lntr.Accept()
if err != nil {
fmt.Println("lntr.Accept err: ", err)
return
}
// 如果有客户端连接,就启动HandleConnect协程处理该连接
go HandleConnect(conn)
}
}
3. 定义manager函数,负责初始化map集合,以及从message管道中读取客户端的消息。
func Manager() {
//初始化map
onlineMap = make(map[string]Client)
//广播消息
for {
msg := <-message
for _, client := range onlineMap {
client.C <- msg
}
}
}
4. 实现HandleConnect函数,负责监听Message通道的数据。
func HandleConnect(conn net.Conn) {
// 关闭客户端连接
defer conn.Close()
// 保存用户到map中
addr := conn.RemoteAddr().String() //获取IP地址
client := Client{make(chan string), addr, addr}
onlineMap[addr] = client
// 启动WriterMsgToClient协程,监听Client通道中数据变化
go WriterMsgToClient(conn, client)
// 登录消息
userInfo := "[" + addr + "]" + addr + ": login\n"
// 向所有用户广播消息
message <- userInfo
// 启动协程,负责读取客户端发送过来消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
fmt.Println("客户端连接已断开!")
return
}
if err != nil {
fmt.Println("conn.Read err: ", err)
return
}
//读取到用户发送的消息
msg := string(buf[:n-1])
userInfo = "[" + client.Addr + "]" + client.Name + ":" + msg + "\n"
//群发消息
message <- userInfo
}
}()
// 让HandleConnect协程不停止
for {
;
}
}
5. 定义WriteMsgToClient函数,负责不断从Client通道中读取消息,然后把读取到的消息发送给客户端。
func WriterMsgToClient(conn net.Conn, client Client) {
for {
msg := <-client.C
conn.Write([]byte(msg))
}
}