Go Language Multi chat Room project

  • 2020-07-21 08:26:59
  • OfStack

This article shares the Go multi-chat room project for your reference, the specific content is as follows

The functional requirements

To achieve single liao Implementation of liao Realize the full network notification of users online Implement user nicknames Realize the storage and view of chat logs

Server-side implementation


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

var (
 // Client information , Use nicknames as keys 
 //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() {

 // Set up server-side listening 
 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 entered the maintenance state, everyone wash and sleep! "))
 }
 listener.Close()
 }()

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

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

 //TODO: each 1 A girlfriend dropped in map
 client := Client{conn, clientName, clientAddr.String()}
 clientsMap[clientName] = client

 //TODO: Send online notifications to users who are already online -- using nicknames 
 for _, client := range clientsMap {
 client.conn.Write([]byte(clientName + " launched "))
 }

 // In a separate coroutine with each 1 Talk to a specific girlfriend 
 go ioWithClient(client)
 }

 // Set the grace exit logic 

}

// with 1 a Client do 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)

 // Will the client say every 1 The sentence is recorded in the file named after him. 
 writeMsgToLog(msg, client)

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

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

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

  // Logging is also done at the destination of point-to-point messages 
  go writeMsgToLog(client.name + ":" + targetMsg,c)
  break
  }
  }
 }

 } else {

 // Client takes initiative to log off 
 if msg == "exit" {
  // Deletes the current client from the online user 
  // Send logoff notifications to other users 
  for name, c := range clientsMap {
  if c == client {
  delete(clientsMap, name)
  } else {
  c.conn.Write([]byte(name + " Get offline "))
  }
  }
 }else if strings.Index(msg,"log@")==0 {
  //log@all
  //log@ Zhang Quan eggs 
  filterName := strings.Split(msg, "@")[1]
  // Send its chat log to the client 
  go sendLog2Client(client,filterName)
 } else {
  client.conn.Write([]byte(" Have read: " + msg))
 }

 }

 }
 }

}

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

 if filterName != "all"{
 // Find chat logs with someone 
 // Filter out the contents with [ filterName# or filterName: 【 line, splice 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 logs 
 // Send to the client 
 client.conn.Write(logBytes)
 }

}

// Will the client say 1 The sentence is recorded in the file named after him. 
func writeMsgToLog(msg string, client Client) {
 // Open the 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()

 // Add 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 a nickname in the command-line arguments 
 nameInfo := [3]interface{}{"name", " John doe ", " 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()
 }()

 // in 1 A separate coroutine is entered and a message is sent 
 go handleSend(conn,name)

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

 // Set the grace exit logic 
 <-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: Send a nickname to the 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 the server 
 _, err := conn.Write(lineBytes)
 CHandleError(err, "conn.Write")

 // The normal exit 
 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)

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

 // Predefined [various types of Pointers that the user may enter] 
 var strValuePtr *string
 var intValuePtr *int

 // Predefined containers for various types of Pointers that the user may enter 
 // The user may enter several string Type, stored in several parameter values string Place these Pointers of the same type in Pointers of the same type map In the 
 // Such as: flag.Parse() In the future, you can use the strValuePtrsMap["cmd"] Take [deposit] "cmd" Pointer to value] 
 var strValuePtrsMap = map[string]*string{}
 var intValuePtrsMap = map[string]*int{}

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

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

 /*
  Start with the name and usage of each command ,
  Both of them string Type, all can pass argArray[i].(string) Get its string with ease 
 1 Called" cmd ", 1 It's called "What do you want?" 
 "cmd"1 Will is used as the map the key
 */
 //[3]interface{}
 //["cmd" " Unknown type " " Do you want to do "]
 //["gid"  0  " Goods to inquire about ID"]
 // The type of shit above [string  It could be any type  string]
 nameValue := argArray[0].(string) // Get the first 1 An element of string value , Is the order of name
 usageValue := argArray[2].(string) // To get the final 1 An element of string Value, which is a command usage

 // judge argArray[1] Specific types of 
 switch argArray[1].(type) {
 case string:
 // Get hold of cmd Pointer to, cmd The value of the at will flag.Parse() It will come later 
 //cmdValuePtr = flag.String("cmd", argArray[1].(string), " Do you want to do ")
 strValuePtr = flag.String(nameValue, argArray[1].(string), usageValue)

 // Put the broken pointer to "cmd" For keys, exist [specially placed string Type of the pointer map , i.e., strValuePtrsMap In the  】  
 strValuePtrsMap[nameValue] = strValuePtr

 case int:
 // Get hold of gid Pointer to, gid The value of the at will flag.Parse() It will come later 
 //gidValuePtr = flag.String("gid", argArray[1].(int), " goods ID")
 intValuePtr = flag.Int(nameValue, argArray[1].(int), usageValue)

 // Put the broken pointer to "gid" For keys, exist [specially placed int Type of the pointer map , i.e., intValuePtrsMap In the  】  
 intValuePtrsMap[nameValue] = intValuePtr
 }

 }

 /*
  When the program runs here, all the different types of stored Pointers are placed on the corresponding types map In the 
 flag.Parse() After, you can from map Gets the "save value pointer" with the parameter name, and then gets the "user input value". 
 */

 // User input finished, parse, [user input value] are all put in the corresponding [saved value pointer] 
 flag.Parse()

 /*
  Traversing all possible types of stored Pointers map 】 
 */
 if len(strValuePtrsMap) > 0 {
 // From the cmd To store a pointer to a value map Take  】  cmd The value of theta and the value of theta cmd Store the result for the key map In the 
 for k, vPtr := range strValuePtrsMap {
 retValuesMap[k] = *vPtr
 }
 }
 if len(intValuePtrsMap) > 0 {
 // From the gid To store a pointer to a value map Take  】  gid The value of theta and the value of theta gid Store the result for the key map In the 
 for k, vPtr := range intValuePtrsMap {
 retValuesMap[k] = *vPtr
 }
 }

 // Returns the result map
 return
}

Related articles: