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 logsServer-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
}