package main
import (
"fmt"
"net"
"strings"
"time"
)
// 用户结构体
type Client struct {
C chan string //用户发送数据的管道
Name string // 用户名
Addr string // 网络地址
}
// 保存在线用户
var onlineMap map[string]Client
// 通讯的管道
var message = make(chan string)
func main() {
//1. 监听
listener, err := net.Listen("tcp", "127.0.0.1:888")
if err != nil {
fmt.Println("net.Listen:", err)
return
}
defer listener.Close()
// 新开一个协成,转发消息,只要有消息来了就遍历map发消息
go Manager()
//2. 主协成,阻塞等待用户
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept:", err)
continue //记住
}
// 处理用户连接
go HandleConn(conn)
}
}
//处理用户连接
func HandleConn(conn net.Conn) {
defer conn.Close()
// 获取用户地址
cliAddr := conn.RemoteAddr().String()
// 创建一个结构体
cli := Client{make(chan string), cliAddr, cliAddr}
// 结构体添加到map
onlineMap[cliAddr] = cli
// 新开一个协成,专门给当前客户端发送信息
go WriteMsgToClient(cli, conn)
// 广播某个用户在线
message <- MakeStr(cli, "login")
// 提示我是谁
cli.C <- MakeStr(cli, "I here")
//定义一个管道判断是否主动退出
isQuit := make(chan bool)
//定义一个通道判断是否超时
isTimeOut := make(chan bool)
// 新开一个协成,接受用户发送过来的数据
go func() {
buf := make([]byte, 1024*2)
for {
n, err := conn.Read(buf)
if n == 0 { // 对方断开
isQuit <- true
fmt.Println("conn.Read:", err)
return
}
// 获取用户信息
userMsg := string(buf[:n-1]) // windows nc测试多个换行
// 处理字符
if len(userMsg) == 3 && userMsg == "who" {
//遍历map给当前用户发送所有成员
conn.Write([]byte("user list:\n"))
for _, tmp := range onlineMap {
userMsg = tmp.Addr + ":" + tmp.Name + "\n"
conn.Write([]byte(userMsg))
}
} else if len(userMsg) >= 8 && userMsg[:6] == "rename" {
// rename|yoyo
name := strings.Split(userMsg, "|")[1] //窃取后面一个
//重新复制
cli.Name = name
onlineMap[cliAddr] = cli
//广播给自己
cli.C <- MakeStr(cli, "rename successfully")
} else {
// 转发此内容
message <- MakeStr(cli, userMsg)
}
// 进来这里就有数据不超时
isTimeOut <- true
}
}()
for {
// 通过select检车channel流动
select {
case <-isQuit:
delete(onlineMap, cliAddr) //移除当前用户
message <- MakeStr(cli, "logout") //广播当前用户下线
return
case <-isTimeOut:
case <-time.After(10 * time.Second):
delete(onlineMap, cliAddr)
message <- MakeStr(cli, "timeout")
return
}
}
}
func Manager() {
// 给map分配空间
onlineMap = make(map[string]Client)
for {
msg := <-message // 没有消息就阻塞
// 遍历map给每个成员发消息
for _, cli := range onlineMap {
cli.C <- msg // 消息给管道!
}
}
}
// 给当前发送消息
func WriteMsgToClient(cli Client, conn net.Conn) {
// 给当前客服端发送信息
for msg := range cli.C {
conn.Write([]byte(msg + "\n"))
}
}
// 制作格式字符串
func MakeStr(cli Client, str string) string {
return "[" + cli.Addr + "]" + cli.Name + ":" + str
}
GO 并发聊天服务器
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...