简单的局域网tcp聊天室

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)
    }
}

猜你喜欢

转载自www.cnblogs.com/huyuan1004/p/11305915.html