SoFunction
Updated on 2025-03-01

Go language multiplayer chat room project practical

This article shares the actual combat of the Go language multi-person chat room project for your reference. The specific content is as follows

Functional Requirements

  • Realize single flirting
  • Realize group flirting
  • Enable users to go online to the full network notification
  • Implement user nickname
  • Implement the storage and viewing of chat logs

Server-side implementation

type Client struct {
 conn 
 name string
 addr string
}

var (
 //Client information, use the nickname key //clientsMap = make(map[string])
 clientsMap = make(map[string]Client)
)

func SHandleError(err error, why string) {
 if err != nil {
 (why, err)
 (1)
 }
}

func main() {

 //Create server monitoring listener, e := ("tcp", "127.0.0.1:8888")
 SHandleError(e, "")
 defer func() {
 for _, client := range clientsMap {
 ([]byte("all: The server is in maintenance state, everyone should wash and go to bed!"))
 }
 ()
 }()

 for {
 //Loop to all girlfriends conn, e := ()
 SHandleError(e, "")
 clientAddr := ()

 //TODO: Receive and save the nickname buffer := make([]byte, 1024)
 var clientName string
 for {
 n, err := (buffer)
 SHandleError(err, "(buffer)")
 if n > 0 {
 clientName = string(buffer[:n])
 break
 }
 }
 (clientName + "Online")

 //TODO: Throw every girlfriend into the map client := Client{conn, clientName, ()}
 clientsMap[clientName] = client

 //TODO: Send online notifications to users who are already online - use a nickname for _, client := range clientsMap {
 ([]byte(clientName + "Online"))
 }

 //Chain with each specific girlfriend in a separate coroutine go ioWithClient(client)
 }

 //Set elegant exit logic
}

//Do IO with a Clientfunc ioWithClient(client Client) {
 //clientAddr := ().String()
 buffer := make([]byte, 1024)

 for {
 n, err := (buffer)
 if err !=  {
 SHandleError(err, "")
 }

 if n > 0 {
 msg := string(buffer[:n])
 ("%s:%s\n", , msg)

 //Record every word the client says in [a file named after him] writeMsgToLog(msg, client)

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

 //The target nickname to be sent targetName := strs[0]
 targetMsg := strs[1]

 //TODO: Use the nickname to locate the target client's Conn if targetName == "all" {
  //Send a mass message  for _, c := range clientsMap {
  ([]byte( + ":" + targetMsg))
  }
 } else {
  //Peer-to-point message  for key, c := range clientsMap {
  if key == targetName {
  ([]byte( + ":" + targetMsg))

  //Logins are also recorded on the target end of the point-to-point message  go writeMsgToLog( + ":" + targetMsg,c)
  break
  }
  }
 }

 } else {

 //The client takes the initiative to go offline if msg == "exit" {
  //Dename the current client from the online user  //Send offline notifications to other users  for name, c := range clientsMap {
  if c == client {
  delete(clientsMap, name)
  } else {
  ([]byte(name + "Offline"))
  }
  }
 }else if (msg,"log@")==0 {
  //log@all
  //log@Zhang Quandan  filterName := (msg, "@")[1]
  //Send its chat log to the client  go sendLog2Client(client,filterName)
 } else {
  ([]byte("Readed:" + msg))
 }

 }

 }
 }

}

//Send its chat log to the clientfunc sendLog2Client(client Client,filterName string) {
 //Read the chat log logBytes, e := ("D:/BJBlockChain1801/demos/W4/day1/01ChatRoomII/logs/" +  + ".log")
 SHandleError(e,"")

 if filterName != "all"{
 //Find chat history with someone //Filter out the content with [filterName# or filterName:] lines, spliced ​​​​​together logStr := string(logBytes) targetStr := ""
 lineSlice := (logStr, "\n")
 for _,lineStr := range lineSlice{
 if len(lineStr)>20{
 contentStr := lineStr[20:]
 if (contentStr,filterName+"#")==0 || (contentStr,filterName+":")==0{
  targetStr += lineStr+"\n"
 }
 }
 }
 ([]byte(targetStr))
 }else{
 //Query all chat history //Send to the client (logBytes)
 }

}

//Record a sentence the client said in [a file named after him]func writeMsgToLog(msg string, client Client) {
 //Open the file file, e := (
 "D:/BJBlockChain1801/demos/W4/day1/01ChatRoomII/logs/"++".log",
 os.O_CREATE|os.O_WRONLY|os.O_APPEND,
 0644)
 SHandleError(e, "")
 defer ()

 //Add this sentence logMsg := (().Format("2006-01-02 15:04:05"), msg)
 ([]byte(logMsg))
}

Client implementation

import (
 "net"
 "fmt"
 "os"
 "bufio"
 "io"
 "flag"
)

var (
 chanQuit = make(chan bool, 0)
 conn  
)

func CHandleError(err error, why string) {
 if err != nil {
 (why, err)

 (1)
 }
}

func main() {

 //TODO: Carry nicknames in command line parameters nameInfo := [3]interface{}{"name", "Anonymous", "Nick name"}
 retValuesMap := GetCmdlineArgs(nameInfo)
 name := retValuesMap["name"].(string)

 //Dial-up connection to get connection var e error
 conn, e = ("tcp", "127.0.0.1:8888")
 CHandleError(e, "")
 defer func() {
 ()
 }()

 //Enter into a separate coroutine and send a message go handleSend(conn,name)

 //Receive server messages in an independent coroutine go handleReceive(conn)

 //Set elegant exit logic <-chanQuit

}

func handleReceive(conn ) {
 buffer := make([]byte, 1024)
 for {
 n, err := (buffer)
 if err !=  {
 CHandleError(err, "")
 }

 if n > 0 {
 msg := string(buffer[:n])
 (msg)
 }
 }

}

func handleSend(conn ,name string) {
 //TODO: Send the nickname to the server _, err := ([]byte(name))
 CHandleError(err,"([]byte(name))")

 reader := ()
 for {
 //Read standard input lineBytes, _, _ := ()

 //Send to the server _, err := (lineBytes)
 CHandleError(err, "")

 //Exit normally if string(lineBytes) == "exit" {
 (0)
 }

 }
}

func GetCmdlineArgs(argInfos ...[3]interface{}) (retValuesMap map[string]interface{}) {

 ("type=%T,value=%v\n", argInfos, argInfos)

 //Initialization returns result retValuesMap = map[string]interface{}{}

 //Predefined [various types of pointers that users may enter] var strValuePtr *string
 var intValuePtr *int

 //Predefined containers with [various types of pointers that users may enter] //The user may enter several string-type parameter values ​​and store them in several string-type pointers. These same type pointers are placed in maps of the same type //For example: After (), you can get the pointer to store the "cmd" value according to [strValuePtrsMap["cmd"]] var strValuePtrsMap = map[string]*string{}
 var intValuePtrsMap = map[string]*int{}

 /* var floatValuePtr *float32
 var floatValuePtrsMap []*float32
 var boolValuePtr *bool
 var boolValuePtrsMap []*bool*/

 //Transfer all command definitions that the user needs to accept for _, argArray := range argInfos {

 /*
  First take out the name and usage of each command.
  Both of these are string type, and all of them can be easily and happily obtained through argArray[i].(string)
  One is called "cmd", the other is called "What do you want to do"
  "cmd" will be used as the map key in a while
  */
 //[3]interface{}
 //["cmd" "Unknown type" "What do you want to do"] //["gid" 0 "Product ID to be queried"] //The type of broken thing above [string may be any type string] nameValue := argArray[0].(string) //Get the string value of the first element, which is the command name usageValue := argArray[2].(string) //Get the string value of the last element, which is the command usage
 //Judge the specific type of argArray[1] switch argArray[1].(type) {
 case string:
 //Get [Storage cmd pointer], the value of cmd will not be available after () //cmdValuePtr = ("cmd", argArray[1].(string), "What do you want to do") strValuePtr = (nameValue, argArray[1].(string), usageValue)

 //Put this broken pointer with "cmd" as the key and exists in [a map that specifically places string-type pointer, that is, strValuePtrsMap] strValuePtrsMap[nameValue] = strValuePtr

 case int:
 //Get [Pointer to store gid], the value of gid will not be available after () //gidValuePtr = ("gid", argArray[1].(int), "Product ID") intValuePtr = (nameValue, argArray[1].(int), usageValue)

 //Put this broken pointer with "gid" as the key and exists in [a map that specifically places an int-type pointer, that is, intValuePtrsMap] intValuePtrsMap[nameValue] = intValuePtr
 }

 }

 /*
  The program runs here, and all different types of [value pointers] are placed in maps of the corresponding type.
  After () it can obtain the [Save Value Pointer] from the map with the parameter name, and then obtain the [Value input by the user]
  */

 //The user has finished entering, parsing, [value entered by the user] is placed in the corresponding [value pointer] ()

 /*
  Iterate through various possible types of [map of stored value pointer]
  */
 if len(strValuePtrsMap) > 0 {
 //Get the value of cmd from the [cmd value storage pointer map], and also use cmd as the key to store it in the result map for k, vPtr := range strValuePtrsMap {
 retValuesMap[k] = *vPtr
 }
 }
 if len(intValuePtrsMap) > 0 {
 //Get the gid value from [Gid value storage pointer map], and use gid as the key to save the result map for k, vPtr := range intValuePtrsMap {
 retValuesMap[k] = *vPtr
 }
 }

 //Return result map return
}

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.