91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

使用Go語言怎么實現一個多人聊天室

發布時間:2021-06-11 17:20:18 來源:億速云 閱讀:173 作者:Leah 欄目:編程語言

今天就跟大家聊聊有關使用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語言怎么實現一個多人聊天室有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

饶阳县| 承德市| 铜陵市| 会泽县| 康平县| 双鸭山市| 饶河县| 永清县| 华宁县| 师宗县| 洞头县| 桓仁| 宜章县| 德清县| 万山特区| 云和县| 金川县| 奉贤区| 浠水县| 大新县| 巴林右旗| 泰和县| 天气| 鄂托克前旗| 周宁县| 湘乡市| 綦江县| 永平县| 怀集县| 崇阳县| 横峰县| 洮南市| 田东县| 宁德市| 南康市| 曲沃县| 前郭尔| 义马市| 金山区| 施甸县| 芦溪县|