go語言併發聊天室
阿新 • • 發佈:2022-04-03
小記一下
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 >") } }