package main
import (
"fmt"
"net"
"strings"
)
//创建用户结构体(包含用户通道)
type User struct {
name string
id string
msg chan string
}
//创建map存储用户
var AllUsers = make(map[string]User, 10)
//var AllUsers = make([]byte, 1024)
//创建全局通道(客户端在全局管道中收发消息)
var message = make(chan string, 1024)
//封装err(简化文中多次if err != nil的情况)
func checkErr(err error) {
if err != nil {
fmt.Println(err)
}
return
}
func main() {
//创建服务
listen, err := net.Listen("tcp", ":8088")
checkErr(err)
//启动全局监听(将客户端发送的数据发送到所有的客户端)
go broadcast()
//监听服务(for循环表示持续监听)
for true {
//创建连接
conn, err := listen.Accept()
checkErr(err)
go headler(conn)
}
}
func headler(conn net.Conn) {
//获取瀛湖ip和port作为参数,传给newUser
ctAddr := conn.RemoteAddr().String()
//创建一个用户实例(管道有缓冲,防止读管道发生阻塞)
newUser := User{ctAddr, ctAddr, make(chan string, 1024)}
//把用户存储到map中或者slice中
AllUsers[newUser.id] = newUser
//AllUsers = append(AllUsers,newUser)如果使用切片,在声明AllUsers时,应当声明为slice
//server端接收用户发送的数据(需要先创建一个buf用来接收和读取用户发送的数据)
buf := make([]byte, 1024)
cnt, err := conn.Read(buf)
checkErr(err)
//客户端发送数据到管道中,自己监听管道将数据再写回自己的客户端
go ClientMsg(&newUser, conn)
//业务逻辑处理
//上线通知(将登陆信息发送到广播的管道中,所有客户端都可以读取的到)
loginfo := fmt.Sprintf("%s:%s上线了", newUser.name, newUser.id)
message <- loginfo
//命令:who获取用户输入数据,判断是否符合命令关键字(实现查询所有登陆的用户)
input := string(buf[:cnt-1])
userdata := make([]string,128)
if input == "\\who" && len(input) == 4 {
for _, users := range AllUsers {
userdata = append(userdata,users.name)
}
r := strings.Join(userdata, "\n")
//将查询出来的数据写入用户的管道中,用户自己调用ClientMsg方法,将数据返回到客户端
newUser.msg <- r
}else if cnt == 0 || input == "\\quit" {
//在用户池中删除用户
delete(AllUsers,newUser.id)
//通知所有用户,某一个用户退出当前系统
logout := fmt.Sprintf("%s退出群聊",newUser.name)
message<-logout
//关闭连接
conn.Close()
//return
}else {
message<-input
}
}
//广播(从管道中读取数据把数据发送给所有客户端的管道中,这部分数据所有人可见,包含所有客户端的数据)
func broadcast() {
info := <-message
for _, user := range AllUsers {
user.msg <- info
}
}
//客户端监听各自的管道中的数据(这部分数据只包含自己管道中的数据,仅自己可见)
func ClientMsg(user *User, conn net.Conn) {
//user.msg应为有缓冲,否则在此处会阻塞
for data := range user.msg {
//把数据写回客户端
_, _ = conn.Write([]byte(data))
}
}