package main
import (
"net"
"fmt"
"strings"
"time"
)
//创建用户结构体类型
type Client struct {
C chan string
Name string
Addr string
}
//创建全局map,存储在线用户
var onlineMap map[string]Client
//创建全局channel,传递用户消息。
var message = make(chan string)
func Manager() {
//初始化onlineMap
onlineMap = make(map[string]Client)
fmt.Println("全局 onlineMap 初始化完成。")
//循环监听全局channel中是否有数据
for {
msg := <-message
fmt.Println("当前在线用户数 -> ", len(onlineMap))
//循环发送消息给所有用户
for _, cli := range onlineMap {
cli.C <- msg
}
}
}
func HandlerConnect(conn net.Conn) {
defer conn.Close()
//创建channel判断,用户是否活跃
hasData := make(chan bool)
//获取用户网络地址
cliAddr := conn.RemoteAddr().String()
//创建新连接用户的结构体信息,默认用户名是IP+Port
cli := Client{make(chan string), cliAddr, cliAddr}
//将新连接用户添加到在线用户map中
onlineMap[cliAddr] = cli
//创建专门用来给当前用户发送消息的go程
go WriteMagToClient(cli, conn)
// 发送用户上线消息到全局channel
msg := MakeMsg(cli, "login")
message <- msg
fmt.Println(msg)
//创建一个channel,用来判断用户退出状态
isQuit := make(chan bool)
//创建一个匿名go程,专门处理用户发送的消息
go func() {
for {
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if n == 0 {
fmt.Printf("服务器检查到客户端[%s]退出。\n", cli.Name)
isQuit <- true //打开用户退出开关
return
} else if err != nil {
fmt.Println("conn.Read", err)
return
}
//读到用户的消息保存到msg中,去掉最后2个\r\n
msg := string(buf[:n-2])
fmt.Println(cliAddr, " -> ", msg)
//提取在线用户列表
if strings.HasPrefix(strings.ToLower(msg), "who") && strings.HasSuffix(strings.ToLower(msg), "who") {
conn.Write([]byte("Online User List:\n"))
//遍历当前map,获取在线用户
for _, cli := range onlineMap {
userInfo := cli.Addr + " -> " + cli.Name + "\n"
conn.Write([]byte(userInfo))
}
} else if strings.HasPrefix(strings.ToLower(msg), "rename|") {
//在线用户改名
name := strings.Split(msg, "|")[1]
cli.Name = name
onlineMap[cliAddr] = cli
conn.Write([]byte("User Rename Success\n"))
} else {
//将读到的用户消息,广播给所有在线用户
message <- MakeMsg(cli, msg)
}
hasData <- true
}
}()
//保存不退出
for {
//监听channel上的数据流动
select {
case <-isQuit:
close(cli.C) //结束子go程(WriteMagToClient函数)
delete(onlineMap, cliAddr) //将用户从在线用户列表移除
message <- MakeMsg(cli, "logout")
return
case <-hasData:
//什么都不做,目的是重置下面case的计时器
case <-time.After(60 * time.Second):
close(cli.C) //结束子go程(WriteMagToClient函数)
delete(onlineMap, cliAddr) //超时退出
message <- MakeMsg(cli, "time out")
return
}
}
}
func WriteMagToClient(cli Client, conn net.Conn) {
//监听用户自带channel上是否有消息
for msg := range cli.C {
conn.Write([]byte(msg + "\n"))
}
}
func MakeMsg(cli Client, msg string) (str string) {
str = "[" + cli.Addr + "]" + cli.Name + " -> " + msg
return str
}
func main() {
//创建监听套接字
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
defer listener.Close()
fmt.Println("TCP服务器 Listen 创建完成。")
//创建管理者go程,管理map和全局channel
go Manager()
//循环监听客户端连接请求
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept err:", err)
return
}
//启动go程处理客户端数据请求
go HandlerConnect(conn)
}
}
package main
import (
"os"
"fmt"
"net"
)
func main() {
//指定服务器通信协议、IP地址、端口号
conn, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println(err)
return
}
defer conn.Close() //关闭
//获取用户键盘输入数据
go func() {
buf := make([]byte, 4096)
for {
n, err := os.Stdin.Read(buf)
if err != nil {
fmt.Println("os.Stdin.Read err:", err)
continue
}
//主动写数据给服务器
conn.Write(buf[:n])
}
}()
buf := make([]byte, 4096)
for {
//接收服务器回发的数据
n, err := conn.Read(buf)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(buf[:n]))
}
}