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