Untitled
unknown
plain_text
a year ago
9.0 kB
4
Indexable
Never
package main import ( "bufio" //"path/filepath" "fmt" //"io" "log" "net" "os" "strconv" "strings" "sync" "time" ) const ( HOST = "localhost" TYPE = "tcp" MaxNumConns = 10 BUFFERSIZE = 1024 ) var ( linuxLogo = ` _nnnn_ dGGGGMMb @p~qp~~qMb M|@||@) M| @,----.JM| JS^\__/ qKL dZP qKRb dZP qKKb fZP SMMb HZM MMMM FqM MMMM __| ". |\dS"qML | ` + "`" + `. | ` + "`" + `' \Zq _) \.___.,| .' \____ )MMMMMP| .' ` + "`" + `-' ` + "`" + `--' ` clients = make(map[net.Conn]struct{}) clientsMux sync.Mutex clientNames = make(map[net.Conn]string) wg sync.WaitGroup PORT int = 2522 messages []string messagesMux sync.Mutex ) func main() { if len(os.Args) > 1 { PORT, _ = strconv.Atoi(os.Args[1]) if len(os.Args) > 2 { fmt.Println("[USAGE]: ./TCPChat $port || go run . $port") return } } fmt.Println("listening on port", PORT) file, err := os.Create("logfile.txt") if err != nil { fmt.Println("ERROR CREATIN FILE") return } defer file.Close() originalStdout := os.Stdout os.Stdout = file fmt.Println("Port: ", PORT) listen, err := net.Listen(TYPE, HOST+":"+strconv.Itoa(PORT)) if err != nil { log.Fatal(err) os.Exit(1) } fmt.Println("listening on port", PORT) // close listener defer listen.Close() for { if len(clients) >= MaxNumConns { fmt.Println("Max number of connections reached. Rejecting new connections...") conn, err := listen.Accept() if err != nil { log.Println(err) continue } conn.Close() continue } conn, err := listen.Accept() if err != nil { log.Fatal(err) os.Exit(1) } wg.Add(1) go handleIncomingRequest(conn) } os.Stdout = originalStdout } /* func sendFileToClient(connection net.Conn) { reader := bufio.NewReader(connection) // Receive file path from client srcFilePath, err := reader.ReadString('\n') if err != nil { log.Println(err) return } srcFilePath = strings.TrimSpace(srcFilePath) if srcFilePath == "" { connection.Write([]byte("Please specify filepath\n")) return } connection.Write([]byte(fmt.Sprintf("File transmission incoming, the file name is: %s. Type accept to accept file transmission \n", filepath.Base(srcFilePath)))) reader = bufio.NewReader(connection) input, err := reader.ReadString('\n') if err != nil { log.Println(err) return } input = strings.TrimSpace(input) if input != "accept" { connection.Write([]byte("You have declined the file transfer ")) return } originalFile, err := os.Open(srcFilePath) if err != nil { fmt.Println(err) return } var copyName string defer originalFile.Close() if len(os.Args) == 1 { connection.Write([]byte("\n You haven't specified the name of the file")) return } // Create a new file to store the copy copyFile, err := os.Create(copyName) if err != nil { fmt.Println(err) return } defer copyFile.Close() fileInfo, err := originalFile.Stat() if err != nil { fmt.Println(err) return } fileSize := fillString(strconv.FormatInt(fileInfo.Size(), 10), 10) fileName := fillString(fileInfo.Name(), 64) fmt.Println("Sending filename and filesize!") connection.Write([]byte(fileSize)) connection.Write([]byte(fileName)) sendBuffer := make([]byte, BUFFERSIZE) fmt.Println("Start sending file!") for { n, err := originalFile.Read(sendBuffer) if err == io.EOF { break } if n > 0 { connection.Write(sendBuffer[:n]) copyFile.Write(sendBuffer[:n]) // Write the bytes directly to the copy file } } fmt.Println("File has been sent and copied, closing connection!") } */ func handleIncomingRequest(conn net.Conn) { defer func() { clientsMux.Lock() delete(clients, conn) disconnectedName := clientNames[conn] delete(clientNames, conn) clientsMux.Unlock() // Notify other clients about the disconnection heDisconnected := fmt.Sprintf("\n%s has left the chat\n", disconnectedName) notifyClients(heDisconnected, conn, false) conn.Write([]byte(fmt.Sprintf("You have disconnected, press enter to close connection and return."))) conn.Close() fmt.Println("Client disconnected:", conn.RemoteAddr()) wg.Done() }() fmt.Println("Client connected:", conn.RemoteAddr()) // Add the client to the clients map clientsMux.Lock() clients[conn] = struct{}{} clientsMux.Unlock() // Send the initial response response := fmt.Sprintf("Welcome to TCP-Chat!\n%s\n", linuxLogo) conn.Write([]byte(response)) reader := bufio.NewReader(conn) timeStr := time.Now().Format("Monday, 02-Jan-06 15:04:05 MST") // Process the name var name string var enteredName = false for !enteredName { conn.Write([]byte("[Enter your name]: ")) // Read the user's input input, err := reader.ReadString('\n') if err != nil { log.Println(err) break } name = strings.TrimSpace(input) if name == "" { conn.Write([]byte("Your name cannot be empty\n")) continue } if nameExists(name) { conn.Write([]byte("Name already exists. Please choose a different name.\n")) continue } // Respond to the client conn.Write([]byte(fmt.Sprintf("Hello, %s!\n", name))) // conn.Write([]byte("Press enter to continue to the chatroom\n")) break } fmt.Println("Name:", name) clientNames[conn] = name enteredName = true // Respond to the client conn.Write([]byte(fmt.Sprintf("Hello, %s! You are now in the chatroom.\n", name))) // conn.Write([]byte("Press enter to continue.\n")) //defaultEntry := fmt.Sprintf("[%s][%s]: ", timeStr, name) userJoin := fmt.Sprintf("\n User '%s' joined the chatroom.\n", name) messagesMux.Lock() for _, message := range messages { if enteredName { clearLines(conn, 1) } conn.Write([]byte(message)) } messagesMux.Unlock() // Notify other clients about the new user joining notifyClients(userJoin, conn, true) for { timeStr = time.Now().Format("Monday, 02-Jan-06 15:04:05 MST") ownDetails := fmt.Sprintf("[%s][%s]: ", timeStr, name) conn.Write([]byte(ownDetails)) message, err := reader.ReadString('\n') if err != nil { log.Println(err) break } // Process the received message message = strings.TrimSpace(message) if message == "exit" { break } /* else if message == "send" { for client := range clients { if client != conn { sendFileToClient(client) } } } */ if strings.HasPrefix(message, "change name") { newName := strings.TrimSpace(strings.TrimPrefix(message, "change name")) clientsMux.Lock() oldName := clientNames[conn] clientsMux.Unlock() clientsMux.Lock() clientNames[conn] = newName clientsMux.Unlock() conn.Write([]byte(fmt.Sprintf("You have changed your name to %s\n", newName))) nameChangeMessage := fmt.Sprintf("\n%s changed his name to %s\n", oldName, newName) notifyClients(nameChangeMessage, conn, false) name = newName continue } // Update the receiver's details receiverTimeStr := time.Now().Format("Monday, 02-Jan-06 15:04:05 MST") receiverName := name messageWithDetails := fmt.Sprintf("\n[%s][%s]: %s\n", receiverTimeStr, receiverName, message) // Broadcast the message to other clients if message != "" { messagesMux.Lock() messages = append(messages, messageWithDetails) messagesMux.Unlock() notifyClients(messageWithDetails, conn, false) fmt.Println("[" + timeStr + "]" + "[" + name + "]: " + message) } } } func nameExists(name string) bool { for _, existingName := range clientNames { if existingName == name { return true } } return false } func notifyClients(message string, sender net.Conn, entry bool) { clientsMux.Lock() defer clientsMux.Unlock() for client := range clients { if client != sender { if !entry { clearLines(client, 1) } clientTimeStr := time.Now().Format("Monday, 02-Jan-06 15:04:05 MST") receiverName := clientNames[client] if receiverName == "" { return } // Construct the default entry with the client's time defaultEntry := fmt.Sprintf("[%s][%s]: ", clientTimeStr, receiverName) // Send the message and default entry to the client client.Write([]byte(message)) client.Write([]byte(defaultEntry)) } } } func clearLines(client net.Conn, lines int) { for i := 0; i < lines; i++ { if clientNames[client] != "" { client.Write([]byte("\033[2K\033[1A")) } } } func fillString(retunString string, toLength int) string { for { lengtString := len(retunString) if lengtString < toLength { retunString = retunString + ":" continue } break } return retunString }