最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語(yǔ)言多人聊天室項(xiàng)目實(shí)戰(zhàn)

 更新時(shí)間:2019年08月20日 15:14:33   作者:尹成  
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言多人聊天室項(xiàng)目實(shí)戰(zhàn),實(shí)現(xiàn)單撩或多撩等多種功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

本文為大家分享了Go語(yǔ)言多人聊天室項(xiàng)目實(shí)戰(zhàn),供大家參考,具體內(nèi)容如下

功能需求

  • 實(shí)現(xiàn)單撩
  • 實(shí)現(xiàn)群撩
  • 實(shí)現(xiàn)用戶上線的全網(wǎng)通知
  • 實(shí)現(xiàn)用戶昵稱
  • 實(shí)現(xiàn)聊天日志的存儲(chǔ)和查看

服務(wù)端實(shí)現(xiàn)

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() {

 //建立服務(wù)端監(jiān)聽(tīng)
 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:服務(wù)器進(jìn)入維護(hù)狀態(tài),大家都洗洗睡吧!"))
 }
 listener.Close()
 }()

 for {
 //循環(huán)接入所有女朋友
 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:將每一個(gè)女朋友丟入map
 client := Client{conn, clientName, clientAddr.String()}
 clientsMap[clientName] = client

 //TODO:給已經(jīng)在線的用戶發(fā)送上線通知——使用昵稱
 for _, client := range clientsMap {
 client.conn.Write([]byte(clientName + "上線了"))
 }

 //在單獨(dú)的協(xié)程中與每一個(gè)具體的女朋友聊天
 go ioWithClient(client)
 }

 //設(shè)置優(yōu)雅退出邏輯

}

//與一個(gè)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)

 //將客戶端說(shuō)的每一句話記錄在【以他的名字命名的文件里】
 writeMsgToLog(msg, client)

 strs := strings.Split(msg, "#")
 if len(strs) > 1 {
 //all#hello
 //zqd#hello

 //要發(fā)送的目標(biāo)昵稱
 targetName := strs[0]
 targetMsg := strs[1]

 //TODO:使用昵稱定位目標(biāo)客戶端的Conn
 if targetName == "all" {
  //群發(fā)消息
  for _, c := range clientsMap {
  c.conn.Write([]byte(client.name + ":" + targetMsg))
  }
 } else {
  //點(diǎn)對(duì)點(diǎn)消息
  for key, c := range clientsMap {
  if key == targetName {
  c.conn.Write([]byte(client.name + ":" + targetMsg))

  //在點(diǎn)對(duì)點(diǎn)消息的目標(biāo)端也記錄日志
  go writeMsgToLog(client.name + ":" + targetMsg,c)
  break
  }
  }
 }

 } else {

 //客戶端主動(dòng)下線
 if msg == "exit" {
  //將當(dāng)前客戶端從在線用戶中除名
  //向其他用戶發(fā)送下線通知
  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]
  //向客戶端發(fā)送它的聊天日志
  go sendLog2Client(client,filterName)
 } else {
  client.conn.Write([]byte("已閱:" + msg))
 }

 }

 }
 }

}

//向客戶端發(fā)送它的聊天日志
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"{
 //查找與某個(gè)人的聊天記錄
 //從內(nèi)容中篩選出帶有【filterName#或filterName:】的行,拼接起來(lái)
 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{
 //查詢所有的聊天記錄
 //向客戶端發(fā)送
 client.conn.Write(logBytes)
 }

}

//將客戶端說(shuō)的一句話記錄在【以他的名字命名的文件里】
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))
}

客戶端實(shí)現(xiàn)

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:在命令行參數(shù)中攜帶昵稱
 nameInfo := [3]interface{}{"name", "無(wú)名氏", "昵稱"}
 retValuesMap := GetCmdlineArgs(nameInfo)
 name := retValuesMap["name"].(string)

 //撥號(hào)連接,獲得connection
 var e error
 conn, e = net.Dial("tcp", "127.0.0.1:8888")
 CHandleError(e, "net.Dial")
 defer func() {
 conn.Close()
 }()

 //在一條獨(dú)立的協(xié)程中輸入,并發(fā)送消息
 go handleSend(conn,name)

 //在一條獨(dú)立的協(xié)程中接收服務(wù)端消息
 go handleReceive(conn)

 //設(shè)置優(yōu)雅退出邏輯
 <-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:發(fā)送昵稱到服務(wù)端
 _, err := conn.Write([]byte(name))
 CHandleError(err,"conn.Write([]byte(name))")

 reader := bufio.NewReader(os.Stdin)
 for {
 //讀取標(biāo)準(zhǔn)輸入
 lineBytes, _, _ := reader.ReadLine()

 //發(fā)送到服務(wù)端
 _, 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)

 //初始化返回結(jié)果
 retValuesMap = map[string]interface{}{}

 //預(yù)定義【用戶可能輸入的各種類型的指針】
 var strValuePtr *string
 var intValuePtr *int

 //預(yù)定義【用戶可能輸入的各種類型的指針】的容器
 //用戶可能輸入好幾個(gè)string型的參數(shù)值,存放在好幾個(gè)string型的指針中,將這些同種類型的指針?lè)旁谕N類型的map中
 //例如:flag.Parse()了以后,可以根據(jù)【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 {

 /*
 先把每個(gè)命令的名稱和用法拿出來(lái),
 這倆貨都是string類型的,所有都可以通過(guò)argArray[i].(string)輕松愉快地獲得其字符串
 一個(gè)叫“cmd”,一個(gè)叫“你想干嘛”
 "cmd"一會(huì)會(huì)用作map的key
 */
 //[3]interface{}
 //["cmd" "未知類型" "你想干嘛"]
 //["gid"  0  "要查詢的商品ID"]
 //上面的破玩意類型[string 可能是任意類型 string]
 nameValue := argArray[0].(string) //拿到第一個(gè)元素的string值,是命令的name
 usageValue := argArray[2].(string) //拿到最后一個(gè)元素的string值,是命令的usage

 //判斷argArray[1]的具體類型
 switch argArray[1].(type) {
 case string:
 //得到【存放cmd的指針】,cmd的值將在flag.Parse()以后才會(huì)有
 //cmdValuePtr = flag.String("cmd", argArray[1].(string), "你想干嘛")
 strValuePtr = flag.String(nameValue, argArray[1].(string), usageValue)

 //將這個(gè)破指針以"cmd"為鍵,存在【專門放置string型指針的map,即strValuePtrsMap】中
 strValuePtrsMap[nameValue] = strValuePtr

 case int:
 //得到【存放gid的指針】,gid的值將在flag.Parse()以后才會(huì)有
 //gidValuePtr = flag.String("gid", argArray[1].(int), "商品ID")
 intValuePtr = flag.Int(nameValue, argArray[1].(int), usageValue)

 //將這個(gè)破指針以"gid"為鍵,存在【專門放置int型指針的map,即intValuePtrsMap】中
 intValuePtrsMap[nameValue] = intValuePtr
 }

 }

 /*
 程序運(yùn)行到這里,所有不同類型的【存值指針】都放在對(duì)相應(yīng)類型的map中了
 flag.Parse()了以后,可以從map中以參數(shù)名字獲取出【存值指針】,進(jìn)而獲得【用戶輸入的值】
 */

 //用戶輸入完了,解析,【用戶輸入的值】全都放在對(duì)應(yīng)的【存值指針】中
 flag.Parse()

 /*
 遍歷各種可能類型的【存值指針的map】
 */
 if len(strValuePtrsMap) > 0 {
 //從【cmd存值指針的map】中拿取cmd的值,還以cmd為鍵存入結(jié)果map中
 for k, vPtr := range strValuePtrsMap {
 retValuesMap[k] = *vPtr
 }
 }
 if len(intValuePtrsMap) > 0 {
 //從【gid存值指針的map】中拿取gid的值,還以gid為鍵存入結(jié)果map中
 for k, vPtr := range intValuePtrsMap {
 retValuesMap[k] = *vPtr
 }
 }

 //返回結(jié)果map
 return
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • golang生成RSA公鑰和密鑰的實(shí)現(xiàn)方法

    golang生成RSA公鑰和密鑰的實(shí)現(xiàn)方法

    本文主要介紹了golang生成RSA公鑰和密鑰的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Go語(yǔ)言到底有沒(méi)有引用傳參(對(duì)比 C++ )

    Go語(yǔ)言到底有沒(méi)有引用傳參(對(duì)比 C++ )

    這篇文章主要介紹了Go 到底有沒(méi)有引用傳參(對(duì)比 C++ ),需要的朋友可以參考下
    2017-09-09
  • Go語(yǔ)言中時(shí)間time相關(guān)處理方法詳解

    Go語(yǔ)言中時(shí)間time相關(guān)處理方法詳解

    在Go語(yǔ)言中,time?包是處理時(shí)間和日期的核心,它提供了豐富的函數(shù)和方法,用于顯示、測(cè)量、計(jì)算、格式化、解析時(shí)間等,本文給大家詳細(xì)介紹了Go時(shí)間time相關(guān)處理方法的相關(guān)資料,需要的朋友可以參考下
    2024-10-10
  • gorm+gin實(shí)現(xiàn)restful分頁(yè)接口的實(shí)踐

    gorm+gin實(shí)現(xiàn)restful分頁(yè)接口的實(shí)踐

    本文主要介紹了gorm+gin實(shí)現(xiàn)restful分頁(yè)接口的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • golang使用json格式實(shí)現(xiàn)增刪查改的實(shí)現(xiàn)示例

    golang使用json格式實(shí)現(xiàn)增刪查改的實(shí)現(xiàn)示例

    這篇文章主要介紹了golang使用json格式實(shí)現(xiàn)增刪查改的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • golang之判斷元素是否在數(shù)組內(nèi)問(wèn)題

    golang之判斷元素是否在數(shù)組內(nèi)問(wèn)題

    這篇文章主要介紹了golang之判斷元素是否在數(shù)組內(nèi)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2020-12-12
  • Golang中的http.Server源碼深入分析

    Golang中的http.Server源碼深入分析

    這篇文章主要介紹了Golang中的http.Server源碼,實(shí)現(xiàn)一個(gè)http.Server非常容易,只需要短短幾行代碼,同時(shí)有了協(xié)程的加持,Go實(shí)現(xiàn)的http.Server能夠取得非常優(yōu)秀的性能,下面我們來(lái)分析看看http.Server的源碼
    2023-05-05
  • Go語(yǔ)言LeetCode題解937重新排列日志文件

    Go語(yǔ)言LeetCode題解937重新排列日志文件

    這篇文章主要為大家介紹了Go語(yǔ)言LeetCode題解937重新排列日志文件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Golang捕獲panic堆棧信息的講解

    Golang捕獲panic堆棧信息的講解

    今天小編就為大家分享一篇關(guān)于Golang捕獲panic堆棧信息的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • Go語(yǔ)言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)

    Go語(yǔ)言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)

    在分布式的系統(tǒng)中,因?yàn)樯婕暗綌?shù)據(jù)的傳輸,所以一定會(huì)進(jìn)行數(shù)據(jù)的交換,此時(shí)就要定義數(shù)據(jù)交換的格式,例如二進(jìn)制、Json、Xml等等。本文總結(jié)了Go語(yǔ)言中的數(shù)據(jù)格式,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07

最新評(píng)論

平阳县| 图木舒克市| 桐城市| 阿勒泰市| 遵义市| 大名县| 迁安市| 福安市| 思南县| 巴塘县| 陇西县| 奇台县| 万州区| 贺州市| 万盛区| 扬州市| 沛县| 广西| 阿勒泰市| 蒙山县| 衡阳市| 会理县| 西畴县| 加查县| 泰兴市| 贵南县| 仁怀市| 新晃| 桂东县| 泗洪县| 泸溪县| 星子县| 阳山县| 交城县| 贡觉县| 莲花县| 新野县| 白银市| 舒城县| 太白县| 武清区|