- 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 {
- fmt.Println(msg)
- conn.Write([]byte(msg + "\n"))
- }
- }
- func MakeMsg(clnt Client, msg string) string {
- buf := "[" + clnt.Addr + "]" + clnt.Name + ":" + msg
- return buf
- }
- func manager() {
- // 监听全局 channel 中是否有数据, 存储至 msg, 无数据阻塞
- for {
- msg := <-message
- for _, clnt := range onlineMap {
- clnt.C <- msg
- }
- }
- }
- func HandlerConnect(conn.NET.Conn) {
- defer conn.Close()
- addr := conn.RemoteAddr().String()
- clnt := Client{make(chan string), addr, addr}
- // 将信连接用户, 添加到在线用户 map 中
- onlineMap[addr] = clnt
- // 创建专门用来给当前用户发送消息的 go 程
- go WriteMsgToClient(clnt, conn)
- isQuit := make(chan bool)
- // 发送用户上线消息到全局 channel 中
- message <- MakeMsg(clnt, "login")
- hasData := make(chan bool)
- // 创建一个匿名 go 程, 专门处理用户发送的消息
- go func() {
- buf := make([]byte, 4096)
- for {
- n, err := conn.Read(buf)
- if n == 0 {
- isQuit <- true
- return
- }
- if err != nil {
- fmt.Println("conn.Read err:", err)
- return
- }
- // 将读到的用户消息, 写入到 message 中
- msg := string(buf[:n-1])
- if msg == "who" && len(msg) == 3 {
- conn.Write([]byte("online user list:\n"))
- for _, user := range onlineMap {
- userInfo := user.Addr + ":" + user.Name + "\n"
- conn.Write([]byte(userInfo))
- }
- } else if len(msg)>= 8 && strings.HasPrefix(msg, "rename|") {
- newName := strings.Split(msg, "|")[1]
- for ip, user := range onlineMap {
- if ip == addr {
- continue
- }
- if newName == user.Name {
- conn.Write([]byte("username exists\n"))
- } else {
- clnt.Name = newName
- onlineMap[addr] = clnt
- conn.Write([]byte("rename successfully\n"))
- }
- }
- } else {
- message <- MakeMsg(clnt, msg)
- }
- hasData <- true
- }
- }()
- for {
- select {
- case <-isQuit:
- close(clnt.C) // 关闭 clnt.c 让子协程退出, 因为协程退出, 子协程不会退出
- delete(onlineMap, clnt.Addr)
- message <- MakeMsg(clnt, "logout")
- return
- case <-time.After(time.Second * 10):
- close(clnt.C)
- delete(onlineMap, clnt.Addr)
- message <- MakeMsg(clnt, "logout")
- return
- case <-hasData:
- }
- }
- }
- func main() {
- // 创建监听套接字
- onlineMap = make(map[string]Client)
- listener, err := net.Listen("tcp", "127.0.0.1:8000")
- if err != nil {
- fmt.Println("Listen err", err)
- return
- }
- defer listener.Close()
- go manager()
- for {
- conn, err := listener.Accept()
- if err != nil {
- fmt.Println("Accept Err", err)
- return
- }
- go HandlerConnect(conn)
- }
- }
来自为知笔记 (Wiz) https://www.wiz.cn/i/c5b11ee0
多人聊天室
来源: http://www.bubuko.com/infodetail-3339796.html