1024programmer Java Go language multi-person chat room project practice

Go language multi-person chat room project practice

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

Functional requirements

  • Achieve single flirtation
  • Achieve group teasing
  • Achieve network-wide notification of users going online
  • Implement user nickname
  • Implementing the storage and viewing of chat logs

Server-side implementation

 type Client struct {
  conn net.Conn
  name string
  addr string
 }

 var (
  //Client information, use nickname as key
  //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() {

  //Create server-side monitoring
  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: The server has entered maintenance status, everyone should go to bed!"))
  }
  listener.Close()
  }()

  for {
  //Loop through all girlfriends
  conn, e := listener.Accept()
  SHandleError(e, "listener.Accept")
  clientAddr := conn.RemoteAddr()

  //TODO:Receive and save nickname
  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 + "Online")

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

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

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

  //Set graceful exit logic

 }

 //Do IO with a Client
 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)

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

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

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

  //TODO: Use nickname to locate the Conn of the target client
  if targetName == "all" {
   //Group message
   for _, c := range clientsMap {
   c.conn.Write([]byte(client.name + ":" + targetMsg))
   }
  } else {
   //Peer-to-peer message
   for key, c := range clientsMap {
   if key == targetName {
   c.conn.Write([]byte(client.name + ":" + targetMsg))

   //Also record logs at the target end of point-to-point messages
   go writeMsgToLog(client.name + ":" + targetMsg,c)
   break
   }
   }
  }

  } else {

  //The client actively logs off
  if msg == "exit" {
   //Remove the current client from online users
   //Send offline notification to other users
   for name, c := range clientsMap {
   if c == client {
   delete(clientsMap, name)
   } else {
   c.conn.Write([]byte(name + "Offline"))
   }
   }
  }else if strings.Index(msg,"log@")==0 {
   //log@all
   //log@张 Quandan
   filterName := strings.Split(msg, "@")[1]
   //Send its chat log to the client
   go sendLog2Client(client,filterName)
  } else {
   client.conn.Write([]byte("Read: " + msg))
  }

  }

  }
  }

 }

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

  if filterName != "all"{
  //Find the chat history with a certain person
  // Filter out the lines with [filterName# or filterName:] from the content and splice them together
  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{
  //Query all chat records
  //Send to client
  client.conn.Write(logBytes)
  }

 }

 //Record what the client said in [a file named after him]
 func writeMsgToLog(msg string, client Client) {
  //open a file
  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()

  //Append this sentence
  logMsg := fmt.Sprintln(time.Now().Format("2006-01-02 15:04:05"), msg)
  file.Write([]byte(logMsg))
 }

Client implementation

 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: carry nickname in command line parameters
  nameInfo := [3]interface{}{"name", "Anonymous", "nickname"}
  retValuesMap := GetCmdlineArgs(nameInfo)
  name := retValuesMap["name"].(string)

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

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

  //Receive server messages in a separate coroutine
  go handleReceive(conn)

  //Set graceful exit logic
   0 {
  msg := string(buffer[:n])
  fmt.Println(msg)
  }
  }

 }

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

  reader := bufio.NewReader(os.Stdin)
  for {
  //Read standard input
  lineBytes, _, _ := reader.ReadLine()

  //Send to server
  _, err := conn.Write(lineBytes)
  CHandleError(err, "conn.Write")

  //Exit normally
  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)

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

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

  //Predefine the container of [various types of pointers that the user may input]
  //The user may input several string-type parameter values, which are stored in several string-type pointers. Put these pointers of the same type in the same type of map.
  //For example: after flag.Parse(), 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*/

  //Traverse 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 items are of type string, and their strings can be obtained easily and happily through argArray[i].(string)
  One is called "cmd" and the other is called "What do you want to do?"
  "cmd" will be used as the map key later
  */
  //[3]interface{}
  //["cmd" "Unknown type" "What do you want to do"]
  //["gid" 0 "Product ID to be queried"]
  //The above broken thing type [string may be any type string]
  nameValue := argArray[0].(string) //Get the string value of the first element, which is the name of the command
  usageValue := argArray[2].(string) //Get the string value of the last element, which is the usage of the command

  //Determine the specific type of argArray[1]
  switch argArray[1].(type) {
  case string:
  //Get [pointer to store cmd], the value of cmd will be available after flag.Parse()
  //cmdValuePtr = flag.String("cmd", argArray[1].(string), "What do you want to do")
  strValuePtr = flag.String(nameValue, argArray[1].(string), usageValue)

  //Use "cmd" as the key to store this broken pointer in [a map specially placed for string pointers, that is, strValuePtrsMap]
  strValuePtrsMap[nameValue] = strValuePtr

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

  //Use "gid" as the key for this broken pointer and store it in [a map specially placed for int type pointers, that is, intValuePtrsMap]
  intValuePtrsMap[nameValue] = intValuePtr
  }

  }

  /*
  When the program runs here, all different types of [stored value pointers] are placed in the map of the corresponding type.
  After flag.Parse(), you can get the [storage pointer] from the map using the parameter name, and then get the [value entered by the user]
  */

  //After the user inputs, parse and put all the [values ​​entered by the user] in the corresponding [storage pointer]
  flag.Parse()

  /*
  Traverse various possible types [map of stored value pointers]
  */
  if len(strValuePtrsMap) > 0 {
  //Get the value of cmd from [map of cmd value pointers] and store it in the result map using cmd as the key
  for k, vPtr := range strValuePtrsMap {
  retValuesMap[k] = *vPtr
  }
  }
  if len(intValuePtrsMap) > 0 {
  //Get the value of gid from [map of gid storage pointers] and store it in the result map using gid as the key
  for k, vPtr := range intValuePtrsMap {
  retValuesMap[k] = *vPtr
  }
  }

  //Return the result map
  return
 }

The above is the entire content of this article. I hope it will be helpful to everyone’s study and I hope you will support me a lot.

This article is from the internet and does not represent1024programmerPosition, please indicate the source when reprinting:https://www.1024programmer.com/784275

author: admin

Previous article
Next article

Leave a Reply

Your email address will not be published. Required fields are marked *

Contact Us

Contact us

181-3619-1160

Online consultation: QQ交谈

E-mail: [email protected]

Working hours: Monday to Friday, 9:00-17:30, holidays off

Follow wechat
Scan wechat and follow us

Scan wechat and follow us

Follow Weibo
Back to top
首页
微信
电话
搜索