直接上代码
package main
import (
"fmt"
"net"
"strings"
"time"
)
//创建用户结构体类型
type Cline struct {
C chan string
Name string
Addr string
}
//创建全局map,存储在线用户
var onlineMap map[string]Cline
//创建全局 channel 传递用户消息
var message = make(chan string)
func WriteToConnect(clnt Cline, conn net.Conn) {
//监听用户自带Channel上是否有消息
for msg := range clnt.C {
conn.Write([]byte(msg + "\n"))
}
}
func MakeMsg(clnt Cline, msg string) (buf string) {
buf = "[" + clnt.Addr + "]" + clnt.Name + ": " + msg
return
}
func HandlerConnect(conn net.Conn) {
defer conn.Close()
//创建channel判断用户是否活跃
hasData := make(chan bool)
//创建新链接用户的信息 结构体 默认用户名是IP+port
netAddr := conn.RemoteAddr().String()
clnt := Cline{C: make(chan string), Name: netAddr, Addr: netAddr}
//将新连接用户添加到在线用户map中,key:IP+port
onlineMap[netAddr] = clnt
//创建专门给当前用户发送消息的go程
go WriteToConnect(clnt, conn)
//发送用户上线消息到全局channel中
message <- MakeMsg(clnt, "login")
//创建一个channel,用来判断推出状态
isQuit := make(chan bool)
//创建一个go程,专门处理用户发出的消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
isQuit <- true
fmt.Println("检测到客户端:%s退出 \n", clnt.Name)
}
if err != nil {
fmt.Println("conn.Read err: ", err)
return
}
msg := string(buf[:n-1])
//提取在线用户列表
if msg == "who" && len(msg) == 3 {
conn.Write([]byte("online user list :\n"))
//遍历当前map,获取在线用户
for _, user := range onlineMap {
userInfo := user.Addr + ":" + user.Name + "\n"
conn.Write([]byte(userInfo))
}
} else if len(msg) >= 8 && msg[:6] == "rename" { //判断用户发送了改名命令
newName := strings.Split(msg, "|")[1]
clnt.Name = newName
onlineMap[netAddr] = clnt
conn.Write([]byte("Rename successful\n"))
} else {
//将读到的用户消息,写入到message中,
message <- MakeMsg(clnt, msg)
}
hasData <- true
}
}()
for {
select {
case <-isQuit:
close(clnt.C)
//将用户从在线用户列表移除
delete(onlineMap, clnt.Addr)
message <- MakeMsg(clnt, "logout") //写入用户退出消息到全局channel
return
case <-hasData:
//什么都不做,目的是充值下面case的计时器。
case <-time.After(time.Second * 10):
//将用户从在线用户列表移除
delete(onlineMap, clnt.Addr)
message <- MakeMsg(clnt, "logout") //写入用户退出消息到全局channel
return
}
}
}
func Manager() {
//初始化map
onlineMap = make(map[string]Cline)
for { //监听全局channel中是否有数据,有数据存储至msg,无数据阻塞。
msg := <-message
//循环发送消息给所有在新用户
for _, clnt := range onlineMap {
clnt.C <- msg
}
}
}
func main() {
//创建监听套接字
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("Listener err", err)
return
}
defer listener.Close()
//创建管理者go程,管理map和全局channel
go Manager()
//循环坚挺客户端连接请求
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept err ", err)
return
}
//启动go程处理客户端数据请求
go HandlerConnect(conn)
}
}
运行
//运行服务
go run main.go
//运行客户端
nc 127.0.0.1 8000
//更改用户名
rename|newname
//用户列表
who