思路:
onlineMap保存在线用户 var onlineMap[string]Client
Client的结构:C chan string(存储需要发送给当前用户的信息),Name string,Addr string
message存放所有需要广播的消息
1.主协程:处理用户连接
1)将用户加到了map中
2)告诉所有在线的用户,谁上线了(message<-某人上线了)
2.协程:专门发送消息,传递参数cli
for msg:=range cli.C{
write(msg)
}
3.协程:
for{
msg:=<-message//如果有内容
//遍历onlineMap,若有多个成员
for _,cli:=range onlineMap{
cli.C<-msg
}
}
4.协程:接受用户的请求,把用户发过来的数据转发
用户发过来的数据放到buf
buf<-message
对方下线,把当前用户从map中移除
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 Messager() {
//给map分配空间
onlineMap = make(map[string]Client)
for {
msg := <-message//没有消息的时候就阻塞了
for _, cli := range onlineMap {
cli.C <- msg
}
}
}
//给当前用户发送消息,当前用户的消息在cli.C 里面
func WriteMsgToClient(cli Client, conn net.Conn) {
for msg := range cli.C {
conn.Write([]byte(msg + "\n"))
}
}
//把msg格式化一下
func MakeMsg(cli Client, msg string) (buf string) {
buf = "[" + cli.Addr + "]" + cli.Name + ":" + msg
return
}
//处理用户链接
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 <- MakeMsg(cli, "login")
//提示是谁
cli.C <- MakeMsg(cli, "I am here")
isQuit := make(chan bool)//对方是否主动退出
hasData := make(chan bool)//对方是否有数据发送
//新开一个协程接收用户发送过来的数据
go func() {
buf := make([]byte, 2048)
for {
n, err := conn.Read(buf)
if n == 0 {//对方断开或者出问题
isQuit <- true
fmt.Println("conn.Read err=", err)
return
}
msg := string(buf[:n-1])//Windows,多一个换行,所以-1
if len(msg) == 3 && msg == "who" {
//遍历map,给当前用户发送所有成员
conn.Write([]byte("user list:\n"))
for _, tmp := range onlineMap {
msg = tmp.Addr + ":" + tmp.Name + "\n"
conn.Write([]byte(msg))
}
} else if len(msg) >= 8 && msg[:6] == "rename" {
name := strings.Split(msg, "|")[1]
cli.Name = name
onlineMap[cliAddr] = cli
conn.Write([]byte("rename successfully\n"))
} else {
//转发此内容
message <- MakeMsg(cli, msg)
}
hasData <- true//代表有数据
}
}()
for {
//通过select检测channel的流动
select {
case <-isQuit:
delete(onlineMap, cliAddr)//将当前用户从map中移除
message <- MakeMsg(cli, "login out")//广播谁下线了
return
case <-hasData:
case <-time.After(60 * time.Second)://超时
delete(onlineMap, cliAddr)
message <- MakeMsg(cli, "time out")
return
}
}
}
func main() {
//监听,指明协议,ip地址,端口
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("net.Listen err=", err)
return
}
defer listener.Close()
//新开一个协程,转发消息,只要有消息来了,遍历整个map,给每个成员发送消息
go Messager()
//主协程,循环阻塞等待用户链接
for {
conn, err1 := listener.Accept()
if err1 != nil {
fmt.Println("listener.Accept err=", err1)
continue
}
go HandleConn(conn)//处理用户链接
}
}
结果: