[GO]并发实现聊天室服务器

package main

import (
"net"
"fmt"
"strings"
"time"
)

type Client struct {
C chan string //用户发送数据的通道
Name string //用户名
Addr string //网络地址
}

//保存在线用户 cliaddr ======> client
var onlineMap map[string]Client

var message = make(chan string)

func WriteMsgToClient(cli Client, conn net.Conn) {
for msg := range cli.C {
//给当前客户端发送信息
conn.Write([]byte(msg + " "))
}
}

func MakeMsg(cli Client, msg string) (buf string) {
buf = "[" + cli.Addr + "]" + cli.Name + ": login"
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")

var isQuit = make(chan bool)

hasData := make(chan bool)

go func() {
buf := make([]byte, 2048)
for true {
n, err := conn.Read(buf)
if n == 0 { //对方断开,或者出问题
isQuit <- true
fmt.Println("conn.read err = ", err)
return
}
msg := string(buf[:n-1])
if len(msg) == 3 && msg == "who" {
conn.Write([]byte("user list : "))
for _, tmp := range onlineMap {
msg = tmp.Addr + ":" + tmp.Name + " "
conn.Write([]byte(msg))
}
}else if len(msg) >= 8 && msg[:6] == "rename" {
//rename|mike
name := strings.Split(msg,"|")[1]
cli.Name = name
onlineMap[cliAddr] = cli
conn.Write([]byte("user list : "))
}else {
message <- MakeMsg(cli, msg)
}
}
}()

for true {
select {
case <- isQuit:
delete(onlineMap, cliAddr) //当前用户从map移除
message <- MakeMsg(cli, "log out") //广播谁下线了
return
case <- hasData:
//不做操作
case time.After(60*time.Second): //60秒都没有操作了,超时
delete(onlineMap, cliAddr) //当前用户从map移除
message <- MakeMsg(cli, "tiem out") //广播谁下线了
return
}
}
}

func Manager() {
//给map分配空间map
onlineMap = make(map[string]Client)

for true {
msg := <-message //没有消息前,这里会阻塞
//遍历map,给map每个成员都发送此消息
for _, cli := range onlineMap{
cli.C <- msg
}
}

}

func main() {
//创建监听
listener, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println("net.Listen err = ", err)
return
}

//新开一个协程,用于转发消息,只要有消息到达 ,那就遍历map然后给map每个成员都发送消息
go Manager()

//主协程,循环阻塞等待用户连接
for true {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept err = ", err)
continue //如果这个不发,説不定还有下个发呢
}
//处理用户的连接
go HandleConn(conn)
}
}

执行的结果,当有任何一个新用户登录了,其他所有的用户都会收到登录提醒,这里以ip加端口号的试做为一个唯一标识 

原文地址:https://www.cnblogs.com/baylorqu/p/9687326.html