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.