您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關使用Go語言怎么實現一個多人聊天室,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
功能需求
實現單撩
實現群撩
實現用戶上線的全網通知
實現用戶昵稱
實現聊天日志的存儲和查看
服務端實現
type Client struct { conn net.Conn name string addr string } var ( //客戶端信息,用昵稱為鍵 //clientsMap = make(map[string]net.Conn) clientsMap = make(map[string]Client) ) func SHandleError(err error, why string) { if err != nil { fmt.Println(why, err) os.Exit(1) } } func main() { //建立服務端監聽 listener, e := net.Listen("tcp", "127.0.0.1:8888") SHandleError(e, "net.Listen") defer func() { for _, client := range clientsMap { client.conn.Write([]byte("all:服務器進入維護狀態,大家都洗洗睡吧!")) } listener.Close() }() for { //循環接入所有女朋友 conn, e := listener.Accept() SHandleError(e, "listener.Accept") clientAddr := conn.RemoteAddr() //TODO:接收并保存昵稱 buffer := make([]byte, 1024) var clientName string for { n, err := conn.Read(buffer) SHandleError(err, "conn.Read(buffer)") if n > 0 { clientName = string(buffer[:n]) break } } fmt.Println(clientName + "上線了") //TODO:將每一個女朋友丟入map client := Client{conn, clientName, clientAddr.String()} clientsMap[clientName] = client //TODO:給已經在線的用戶發送上線通知——使用昵稱 for _, client := range clientsMap { client.conn.Write([]byte(clientName + "上線了")) } //在單獨的協程中與每一個具體的女朋友聊天 go ioWithClient(client) } //設置優雅退出邏輯 } //與一個Client做IO func ioWithClient(client Client) { //clientAddr := conn.RemoteAddr().String() buffer := make([]byte, 1024) for { n, err := client.conn.Read(buffer) if err != io.EOF { SHandleError(err, "conn.Read") } if n > 0 { msg := string(buffer[:n]) fmt.Printf("%s:%s\n", client.name, msg) //將客戶端說的每一句話記錄在【以他的名字命名的文件里】 writeMsgToLog(msg, client) strs := strings.Split(msg, "#") if len(strs) > 1 { //all#hello //zqd#hello //要發送的目標昵稱 targetName := strs[0] targetMsg := strs[1] //TODO:使用昵稱定位目標客戶端的Conn if targetName == "all" { //群發消息 for _, c := range clientsMap { c.conn.Write([]byte(client.name + ":" + targetMsg)) } } else { //點對點消息 for key, c := range clientsMap { if key == targetName { c.conn.Write([]byte(client.name + ":" + targetMsg)) //在點對點消息的目標端也記錄日志 go writeMsgToLog(client.name + ":" + targetMsg,c) break } } } } else { //客戶端主動下線 if msg == "exit" { //將當前客戶端從在線用戶中除名 //向其他用戶發送下線通知 for name, c := range clientsMap { if c == client { delete(clientsMap, name) } else { c.conn.Write([]byte(name + "下線了")) } } }else if strings.Index(msg,"log@")==0 { //log@all //log@張全蛋 filterName := strings.Split(msg, "@")[1] //向客戶端發送它的聊天日志 go sendLog2Client(client,filterName) } else { client.conn.Write([]byte("已閱:" + msg)) } } } } } //向客戶端發送它的聊天日志 func sendLog2Client(client Client,filterName string) { //讀取聊天日志 logBytes, e := ioutil.ReadFile("D:/BJBlockChain1801/demos/W4/day1/01ChatRoomII/logs/" + client.name + ".log") SHandleError(e,"ioutil.ReadFile") if filterName != "all"{ //查找與某個人的聊天記錄 //從內容中篩選出帶有【filterName#或filterName:】的行,拼接起來 logStr := string(logBytes) targetStr := "" lineSlice := strings.Split(logStr, "\n") for _,lineStr := range lineSlice{ if len(lineStr)>20{ contentStr := lineStr[20:] if strings.Index(contentStr,filterName+"#")==0 || strings.Index(contentStr,filterName+":")==0{ targetStr += lineStr+"\n" } } } client.conn.Write([]byte(targetStr)) }else{ //查詢所有的聊天記錄 //向客戶端發送 client.conn.Write(logBytes) } } //將客戶端說的一句話記錄在【以他的名字命名的文件里】 func writeMsgToLog(msg string, client Client) { //打開文件 file, e := os.OpenFile( "D:/BJBlockChain1801/demos/W4/day1/01ChatRoomII/logs/"+client.name+".log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) SHandleError(e, "os.OpenFile") defer file.Close() //追加這句話 logMsg := fmt.Sprintln(time.Now().Format("2006-01-02 15:04:05"), msg) file.Write([]byte(logMsg)) }
客戶端實現
import ( "net" "fmt" "os" "bufio" "io" "flag" ) var ( chanQuit = make(chan bool, 0) conn net.Conn ) func CHandleError(err error, why string) { if err != nil { fmt.Println(why, err) os.Exit(1) } } func main() { //TODO:在命令行參數中攜帶昵稱 nameInfo := [3]interface{}{"name", "無名氏", "昵稱"} retValuesMap := GetCmdlineArgs(nameInfo) name := retValuesMap["name"].(string) //撥號連接,獲得connection var e error conn, e = net.Dial("tcp", "127.0.0.1:8888") CHandleError(e, "net.Dial") defer func() { conn.Close() }() //在一條獨立的協程中輸入,并發送消息 go handleSend(conn,name) //在一條獨立的協程中接收服務端消息 go handleReceive(conn) //設置優雅退出邏輯 <-chanQuit } func handleReceive(conn net.Conn) { buffer := make([]byte, 1024) for { n, err := conn.Read(buffer) if err != io.EOF { CHandleError(err, "conn.Read") } if n > 0 { msg := string(buffer[:n]) fmt.Println(msg) } } } func handleSend(conn net.Conn,name string) { //TODO:發送昵稱到服務端 _, err := conn.Write([]byte(name)) CHandleError(err,"conn.Write([]byte(name))") reader := bufio.NewReader(os.Stdin) for { //讀取標準輸入 lineBytes, _, _ := reader.ReadLine() //發送到服務端 _, err := conn.Write(lineBytes) CHandleError(err, "conn.Write") //正常退出 if string(lineBytes) == "exit" { os.Exit(0) } } } func GetCmdlineArgs(argInfos ...[3]interface{}) (retValuesMap map[string]interface{}) { fmt.Printf("type=%T,value=%v\n", argInfos, argInfos) //初始化返回結果 retValuesMap = map[string]interface{}{} //預定義【用戶可能輸入的各種類型的指針】 var strValuePtr *string var intValuePtr *int //預定義【用戶可能輸入的各種類型的指針】的容器 //用戶可能輸入好幾個string型的參數值,存放在好幾個string型的指針中,將這些同種類型的指針放在同種類型的map中 //例如:flag.Parse()了以后,可以根據【strValuePtrsMap["cmd"]】拿到【存放"cmd"值的指針】 var strValuePtrsMap = map[string]*string{} var intValuePtrsMap = map[string]*int{} /* var floatValuePtr *float32 var floatValuePtrsMap []*float32 var boolValuePtr *bool var boolValuePtrsMap []*bool*/ //遍歷用戶需要接受的所有命令定義 for _, argArray := range argInfos { /* 先把每個命令的名稱和用法拿出來, 這倆貨都是string類型的,所有都可以通過argArray[i].(string)輕松愉快地獲得其字符串 一個叫“cmd”,一個叫“你想干嘛” "cmd"一會會用作map的key */ //[3]interface{} //["cmd" "未知類型" "你想干嘛"] //["gid" 0 "要查詢的商品ID"] //上面的破玩意類型[string 可能是任意類型 string] nameValue := argArray[0].(string) //拿到第一個元素的string值,是命令的name usageValue := argArray[2].(string) //拿到最后一個元素的string值,是命令的usage //判斷argArray[1]的具體類型 switch argArray[1].(type) { case string: //得到【存放cmd的指針】,cmd的值將在flag.Parse()以后才會有 //cmdValuePtr = flag.String("cmd", argArray[1].(string), "你想干嘛") strValuePtr = flag.String(nameValue, argArray[1].(string), usageValue) //將這個破指針以"cmd"為鍵,存在【專門放置string型指針的map,即strValuePtrsMap】中 strValuePtrsMap[nameValue] = strValuePtr case int: //得到【存放gid的指針】,gid的值將在flag.Parse()以后才會有 //gidValuePtr = flag.String("gid", argArray[1].(int), "商品ID") intValuePtr = flag.Int(nameValue, argArray[1].(int), usageValue) //將這個破指針以"gid"為鍵,存在【專門放置int型指針的map,即intValuePtrsMap】中 intValuePtrsMap[nameValue] = intValuePtr } } /* 程序運行到這里,所有不同類型的【存值指針】都放在對相應類型的map中了 flag.Parse()了以后,可以從map中以參數名字獲取出【存值指針】,進而獲得【用戶輸入的值】 */ //用戶輸入完了,解析,【用戶輸入的值】全都放在對應的【存值指針】中 flag.Parse() /* 遍歷各種可能類型的【存值指針的map】 */ if len(strValuePtrsMap) > 0 { //從【cmd存值指針的map】中拿取cmd的值,還以cmd為鍵存入結果map中 for k, vPtr := range strValuePtrsMap { retValuesMap[k] = *vPtr } } if len(intValuePtrsMap) > 0 { //從【gid存值指針的map】中拿取gid的值,還以gid為鍵存入結果map中 for k, vPtr := range intValuePtrsMap { retValuesMap[k] = *vPtr } } //返回結果map return }
看完上述內容,你們對使用Go語言怎么實現一個多人聊天室有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。