go语言并发聊天室

小记一下

server端

package main

import (
    "encoding/json"
    "fmt"
    "net"
    "unsafe"
)

//传输的数据结构
type ChatMsg struct {
    From, To, Msg string //从哪里来,到哪里去,内容
}

//与客户端通信的结构
type ClientMsg struct {
    To      string  `json:"to"`       //去哪
    Msg     string  `jason:"msg"`     //内容
    Datalen uintptr `jason:"datalen"` //数据长度
}

//定义消息中心用的channel
var chan_msgcenter chan ChatMsg

//定义用户名到ip的map
var mapName2CliAddr map[string]string

//定义ip到conn的map
var mapCliaddr2Clients map[string]net.Conn

//主函数
func main() {
    //初始化
    mapCliaddr2Clients = make(map[string]net.Conn)
    mapName2CliAddr = make(map[string]string)
    chan_msgcenter = make(chan ChatMsg)
    //监听
    lsner, err := net.Listen("tcp", "localhost:8888")
    if err != nil {
        panic("listening failed!")
    }
    defer lsner.Close()
    //开一个routine起信息中心
    go Msg_center()
    //循环接收消息
    for {
        conn, err := lsner.Accept()
        if err != nil {
            fmt.Println("ACCEPT FAILED!")
            break
        }
        //开一个routine处理消息
        go Handle_conn(conn)
    }
}

//处理断开连接
func Logout(conn net.Conn, from string) {
    defer conn.Close()
    //在连接map里删掉
    delete(mapCliaddr2Clients, from)
    //告诉消息中心,发给大家退出登录的消息
    msg := ChatMsg{from, "all", from + "->logout"}
    chan_msgcenter <- msg
}
func Msg_center() {
    for {
        //等消息传过来,有了就发
        msg := <-chan_msgcenter
        go send_msg(msg)
    }
}

//处理新连接
func Handle_conn(conn net.Conn) {
    //获取ip
    from := conn.RemoteAddr().String()
    //在map里添加键值对 ip->连接
    mapCliaddr2Clients[from] = conn
    //告诉所有人登陆了
    msg := ChatMsg{from, "all", from + "->login"}
    chan_msgcenter <- msg
    defer Logout(conn, from)
    //读取信息
    buf := make([]byte, 256)
    for {
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("reading failed!")
            break
        }
        if n > 0 {
            //将信息jsondecode后放进climsg结构体
            var climsg ClientMsg
            err := json.Unmarshal(buf[:n], &climsg)
            if err != nil {
                fmt.Println("unmarshaling failed!")
                continue
            }
            //检验格式
            if climsg.Datalen != unsafe.Sizeof(climsg) {
                fmt.Println("unvalid format!", climsg)
                continue
            }
            //预设发送对象为all
            chatmsg := ChatMsg{from, "all", climsg.Msg}
            //判断发到哪里,可以通过set来设置用户名
            switch climsg.To {
            case "all":
            case "set":
                mapName2CliAddr[climsg.Msg] = from
                chatmsg.Msg = from + " set name=" + climsg.Msg + "success"
                chatmsg.From = "server"
            default:
                chatmsg.To = climsg.To
            }
            //传给channel
            chan_msgcenter <- chatmsg
        }
    }

}
func send_msg(msg ChatMsg) {
    //先jsonencode一下
    data, err := json.Marshal(msg)
    if err != nil {
        fmt.Println("marshaling failed!")
        return
    }
    //判断发给谁
    if msg.To == "all" {
        for _, v := range mapCliaddr2Clients {
            //不给自己发
            if msg.From != v.RemoteAddr().String() {
                v.Write(data)
            }
        }
    } else {
        //私聊,判断用户存在和是否在线
        from, ok := mapName2CliAddr[msg.To]
        if !ok {
            fmt.Println("USER not EXISTS!", msg.To)
            return
        }
        conn, ok := mapCliaddr2Clients[from]
        if !ok {
            fmt.Println("cli not exists!", from, msg.To)
            return
        }
        conn.Write(data)
    }
}

 

client端

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "net"
    "os"
    "strings"
    "unsafe"
)

//与客户端通信的结构
type ClientMsg struct {
    To      string  `json:"to"`       //去哪
    Msg     string  `jason:"msg"`     //内容
    Datalen uintptr `jason:"datalen"` //数据长度
}

//用户帮助
func Help() {
    fmt.Println("set: your name")
    fmt.Println("all: your msg -- broadcadt")
    fmt.Println("anyone: your msg -- private msg")
}
func main() {
    //连接
    conn, err := net.Dial("tcp", "loaclhost:8888")
    if err != nil {
        panic("dialing failed!")
    }
    defer conn.Close()
    //通信
    go handle_conn(conn)
    //读标准输入
    reader := bufio.NewReader(os.Stdin)
    fmt.Printf("Welcome to YU's pub chat\n")
    Help()
    for {
        fmt.Printf("YU's chat>")
        msg, err := reader.ReadString('\n')
        if err != nil {
            panic("reading string failed!")
        }
        msg = strings.Trim(msg, "\r\n")
        if msg == "quit" {
            fmt.Println("bye~")
            break
        }
        if msg == "help" {
            Help()
            continue
        }
        //消息传输格式处理
        msgs := strings.Split(msg, ":")
        if len(msgs) == 2 {
            var climsg ClientMsg
            climsg.To = msgs[0]
            climsg.Msg = msgs[1]
            climsg.Datalen = unsafe.Sizeof(climsg)
            data, err := json.Marshal(climsg)
            if err != nil {
                fmt.Println("marshaling failed", err, climsg)
                continue
            }
            _, err = conn.Write(data)
            if err != nil {
                fmt.Println("writing failed", err, data)
                break
            }
        }
    }
}
func handle_conn(conn net.Conn) {
    buf := make([]byte, 256)
    for {
        n, err := conn.Read(buf)
        if err != nil {
            panic("reading failed!")
        }
        fmt.Println(string(buf[:n]))
        fmt.Printf("YU's chat >")
    }
}

 

posted @ 2022-04-03 09:07  Yu_so1dier0n  阅读(58)  评论(0编辑  收藏  举报