package main
import (
"fmt"
"net"
"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 WriteMsgToClient(clnt Client, conn net.Conn) {
// 监听用户自带channel上是否有消息
for msg := range clnt.C {
conn.Write([]byte(msg+"\n"))
}
}
func MakeMsg(clnt Client, msg string) (buf string) {
buf = "[" + clnt.Addr + "]" + clnt.Name + ":" + msg
return
}
func HandleConnect(conn net.Conn) {
defer conn.Close()
// 获取用户ip+port
netAddr := conn.RemoteAddr().String()
// 创建用户
clnt := Client{
C:make(chan string),
Name:netAddr,
Addr:netAddr,
}
// 将新连接用户添加到新用户map中,key:ip+port value:Client
onlineMap[netAddr] = clnt
// 创建专门用来给当前用户发送的go程
go WriteMsgToClient(clnt, conn)
// 发送用户上线消息到全局channel中
message <- MakeMsg(clnt, "login")
// 创建一个channel 判断用户退出状态
isQuit := make(chan bool)
// 判断用户是否霍活跃,超时强踢
hasData := make(chan bool)
// 专门处理用户发送的消息
go func () {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
isQuit <- true
fmt.Printf("检测到客户端%s退出\n", clnt.Name)
return
}
if err !=nil {
fmt.Println("conn.Read err:", err)
return
}
msg := string(buf[:n-2]) // -2 处理末尾的\r\n
// 查看当前有多少在线用户
if msg == "who" {
conn.Write([]byte("online user list:\n"))
for _, usr := range onlineMap {
usrInfo := usr.Addr + ":" + usr.Name + "\n"
conn.Write([]byte(usrInfo+"\n"))
}
} else if len(msg) >= 8 && msg[:7] == "rename|" { // 改名
newName := strings.Split(msg, "|")[1]
clnt.Name = newName
onlineMap[netAddr] = clnt
conn.Write([]byte("改名成功"))
} else {
// 将读到的用户消息广播给在线用户
message <- MakeMsg(clnt, msg)
}
hasData <- true
}
}()
for {
// 监听channel上的数据流动 检测到用户退出,将用户删除
select {
case <- isQuit:
delete(onlineMap, clnt.Addr)
message <- MakeMsg(clnt, "logout")
return
case <- hasData:
// 什么都不做 重置下面的计时器
case <- time.After(time.Second * 10):
delete(onlineMap, clnt.Addr)
message <- MakeMsg(clnt, "logout")
return
}
}
}
func Manager() {
//初始化onlineMap
onlineMap = make(map[string]Client)
// 监听全局channel中是否有数据,有数据存储到msg,无数据阻塞
for {
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("net.Listen err:", err)
return
}
defer listener.Close()
// 创建管理者go程,管理map和全局channel
go Manager()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept err:", err)
return
}
// 处理客户端数据请求
go HandleConnect(conn)
}
}
Simple LAN tcp chat room
Guess you like
Origin www.cnblogs.com/huyuan1004/p/11305915.html
Recommended
Ranking