From 622340610ad35a58fcb381078c8db4de8e09c439 Mon Sep 17 00:00:00 2001 From: raul Date: Tue, 2 Apr 2024 11:27:48 +0200 Subject: [PATCH 1/9] Create client.go --- cmd/client.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 cmd/client.go diff --git a/cmd/client.go b/cmd/client.go new file mode 100644 index 0000000..aaf2f4e --- /dev/null +++ b/cmd/client.go @@ -0,0 +1,55 @@ +/* +Copyright © 2024 Raul +*/ + +package cmd + +import ( + "fmt" + "net" + "os" + + "github.com/spf13/cobra" +) + +// clientCmd represents the client command +var clientCmd = &cobra.Command{ + Use: "client", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + client(cmd) + }, +} + +func init() { + rootCmd.AddCommand(clientCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // clientCmd.PersistentFlags().String("foo", "", "A help for foo") + clientCmd.Flags().String("ip", "", "Server to connect to") + clientCmd.Flags().String("port", "1302", "Port to connect to") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // clientCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + +func client(cmd *cobra.Command) { + port, _ := cmd.Flags().GetString("port") + ip, _ := cmd.Flags().GetString("ip") + if ip == "" { + fmt.Println("Not enough arguments, run \"-h\" parameter to see all the parameters!") + os.Exit(1) + } + socket := ip + ":" + port + net.Dial("tcp", socket) +} -- 2.30.2 From 39c2dde0164fbb60135836bd00425f754a8ce91a Mon Sep 17 00:00:00 2001 From: raul Date: Fri, 12 Apr 2024 08:55:59 +0200 Subject: [PATCH 2/9] Update dependencies --- go.mod | 8 +++++++- go.sum | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6e4e6a2..d98cc09 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,15 @@ module mini-chat go 1.22.1 -require github.com/spf13/cobra v1.8.0 +require ( + github.com/jroimartin/gocui v0.5.0 + github.com/spf13/cobra v1.8.0 +) require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/nsf/termbox-go v1.1.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index d0e8c2c..27bb663 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,16 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jroimartin/gocui v0.5.0 h1:DCZc97zY9dMnHXJSJLLmx9VqiEnAj0yh0eTNpuEtG/4= +github.com/jroimartin/gocui v0.5.0/go.mod h1:l7Hz8DoYoL6NoYnlnaX6XCNR62G7J5FfSW5jEogzaxE= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -- 2.30.2 From 6599c66abbceff702c925851de105ac33effb673 Mon Sep 17 00:00:00 2001 From: raul Date: Fri, 12 Apr 2024 11:59:48 +0200 Subject: [PATCH 3/9] Add ui.go file --- cmd/ui.go | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 cmd/ui.go diff --git a/cmd/ui.go b/cmd/ui.go new file mode 100644 index 0000000..41260db --- /dev/null +++ b/cmd/ui.go @@ -0,0 +1,108 @@ +package cmd + +import ( + "fmt" + "github.com/jroimartin/gocui" + "log" +) + +func ui() { + g, err := gocui.NewGui(gocui.OutputNormal) + if err != nil { + log.Panicln(err) + } + defer g.Close() + g.SetManagerFunc(layout) + g.Mouse = true + g.Cursor = true + initKeybindings(g) + + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { + log.Panicln(err) + } +} + +func quit(*gocui.Gui, *gocui.View) error { + return gocui.ErrQuit +} + +func sendmsg(g *gocui.Gui, v *gocui.View) error { + chatbox, err := g.View("chatbox") + textarea, err := g.View("textarea") + if err != nil { + log.Panicln(err) + } + message := textarea.Buffer() + msg := string(message) + if msg == "" { + return nil + } + + fmt.Fprint(chatbox, msg) + textarea.Clear() + textarea.SetCursor(0, 0) + return nil +} + +func initKeybindings(g *gocui.Gui) error { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("button", gocui.MouseLeft, gocui.ModNone, sendmsg); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("textarea", gocui.KeyEnter, gocui.ModNone, sendmsg); err != nil { + log.Panicln(err) + } + return nil +} + +func layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + + if chatbox, err := g.SetView("chatbox", 2, 1, maxX/2+40, maxY-6); err != nil { + if err != gocui.ErrUnknownView { + return err + } + chatbox.Title = "Chat Box" + fmt.Fprintln(chatbox, "Hello world!") + } + + if button, err := g.SetView("button", maxX/2+32, maxY-4, maxX/2+40, maxY-2); err != nil { + if err != gocui.ErrUnknownView { + return err + } + //button.BgColor = gocui.ColorRed + button.Wrap = true + button.Frame = true + fmt.Fprintln(button, "Send it") + } + + if textarea, err := g.SetView("textarea", 2, maxY-4, maxX/2+28, maxY-2); err != nil { + if err != gocui.ErrUnknownView { + return err + } + if _, err := g.SetCurrentView("textarea"); err != nil { + log.Panicln(err) + } + textarea.Title = "Send message" + textarea.Wrap = true + textarea.Editable = true + } + + if currentUsers, err := g.SetView("currentUsers", maxX/2+42, 1, maxX-6, maxY-6); err != nil { + if err != gocui.ErrUnknownView { + return err + } + currentUsers.Title = "Connected users" + } + + // TODO: Add "Users currently online" view + // if currentUsers, err := g.SetView("currentUsers", maxX/2+34, 1, maxX/2+40, maxY/2+11); err != nil { + // + // } + + return nil +} -- 2.30.2 From c4fb5606f6c9c9a793bec98d2b335d13be3335d0 Mon Sep 17 00:00:00 2001 From: raul Date: Fri, 12 Apr 2024 12:00:08 +0200 Subject: [PATCH 4/9] Testing sending messages to server from client --- cmd/client.go | 52 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/cmd/client.go b/cmd/client.go index aaf2f4e..1977808 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -5,6 +5,7 @@ Copyright © 2024 Raul package cmd import ( + "bufio" "fmt" "net" "os" @@ -15,13 +16,10 @@ import ( // clientCmd represents the client command var clientCmd = &cobra.Command{ Use: "client", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, + Short: "Connect to a mini-chat server", + Long: `Connect to a mini-chat server. + Example: + ./mini-chat client --ip 192.168.0.50 --port 1337`, Run: func(cmd *cobra.Command, args []string) { client(cmd) }, @@ -43,13 +41,49 @@ func init() { // clientCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } +func readMsg(conn net.Conn) error { + reply := make([]byte, 1024) + conn.Read(reply) + fmt.Println(string(reply)) + return nil +} + func client(cmd *cobra.Command) { port, _ := cmd.Flags().GetString("port") ip, _ := cmd.Flags().GetString("ip") if ip == "" { fmt.Println("Not enough arguments, run \"-h\" parameter to see all the parameters!") - os.Exit(1) + os.Exit(0) + } + if port == "" { + port = "1302" } socket := ip + ":" + port - net.Dial("tcp", socket) + conn, err := net.Dial("tcp", socket) + cobra.CheckErr(err) + defer conn.Close() + + // go func() { + // for { + // reply := make([]byte, 1024) + // conn.Read(reply) + // fmt.Println(string(reply)) + // } + // }() + + // TODO: GET READING DATA FROM SERVER WORKING + + for { + msg, err := bufio.NewReader(os.Stdin).ReadString('\n') + cobra.CheckErr(err) + conn.Write([]byte(msg)) + go readMsg(conn) + } + + // msg, err := bufio.NewReader(os.Stdin).ReadString('\n') + // fmt.Println(msg) + + //fmt.Println(string(data)) + + //ui() } -- 2.30.2 From 678244d28925affdecc289d85280e4a3244378e5 Mon Sep 17 00:00:00 2001 From: raul Date: Sat, 13 Apr 2024 13:08:02 +0200 Subject: [PATCH 5/9] Update dependencies --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d98cc09..815fe61 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,13 @@ go 1.22.1 require ( github.com/jroimartin/gocui v0.5.0 + github.com/nsf/termbox-go v1.1.1 github.com/spf13/cobra v1.8.0 ) require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/nsf/termbox-go v1.1.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect ) -- 2.30.2 From 75dac0ccef3e0e4254e29c6c4ce03548536a0962 Mon Sep 17 00:00:00 2001 From: raul Date: Sat, 13 Apr 2024 13:08:43 +0200 Subject: [PATCH 6/9] Attempt to clean up server codebase --- cmd/server.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/cmd/server.go b/cmd/server.go index 72da336..823b414 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -90,25 +90,11 @@ func masterReceiver(slaveChannel chan<- string, masterChannel <-chan string) { } } -// func receiver(conn net.Conn, ch chan string) { -// fmt.Println("THE RECEIVER HAS BEEN STARTED, YOU CAN ONLY SEE THIS MESSAGE ONCE") -// receiverIsStarted = false -// for { -// select { -// case otherMessage := <-ch: -// conn.Write([]byte(otherMessage)) -// default: -// } -// } -// } - func handleConn(conn net.Conn, slaveChannel <-chan string, masterChannel chan<- string) { defer conn.Close() - //var otherMessage string go func() { for { - //select { message := <-slaveChannel conn.Write([]byte(message)) } -- 2.30.2 From ca10bc42844c614cb14db0d03d18f6cf0c21043b Mon Sep 17 00:00:00 2001 From: raul Date: Sat, 13 Apr 2024 13:11:36 +0200 Subject: [PATCH 7/9] Implement Gocui UI into mini-chat client --- cmd/client.go | 61 ++++++++++++++++++++++++++++----------------------- cmd/ui.go | 40 +++++++++++++++------------------ 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/cmd/client.go b/cmd/client.go index 1977808..05cce97 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -7,10 +7,11 @@ package cmd import ( "bufio" "fmt" + "github.com/jroimartin/gocui" + "github.com/spf13/cobra" + "log" "net" "os" - - "github.com/spf13/cobra" ) // clientCmd represents the client command @@ -25,6 +26,9 @@ var clientCmd = &cobra.Command{ }, } +var err error +var conn net.Conn + func init() { rootCmd.AddCommand(clientCmd) @@ -41,13 +45,6 @@ func init() { // clientCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } -func readMsg(conn net.Conn) error { - reply := make([]byte, 1024) - conn.Read(reply) - fmt.Println(string(reply)) - return nil -} - func client(cmd *cobra.Command) { port, _ := cmd.Flags().GetString("port") ip, _ := cmd.Flags().GetString("ip") @@ -59,31 +56,39 @@ func client(cmd *cobra.Command) { port = "1302" } socket := ip + ":" + port - conn, err := net.Dial("tcp", socket) + conn, err = net.Dial("tcp", socket) cobra.CheckErr(err) defer conn.Close() - // go func() { - // for { - // reply := make([]byte, 1024) - // conn.Read(reply) - // fmt.Println(string(reply)) - // } - // }() + reply := make([]byte, 1024) + conn.Read(reply) + fmt.Print(string(reply)) - // TODO: GET READING DATA FROM SERVER WORKING + msg, err := bufio.NewReader(os.Stdin).ReadString('\n') + cobra.CheckErr(err) + conn.Write([]byte(msg)) - for { - msg, err := bufio.NewReader(os.Stdin).ReadString('\n') - cobra.CheckErr(err) - conn.Write([]byte(msg)) - go readMsg(conn) + ui(conn) + +} + +func sendmsg(g *gocui.Gui, v *gocui.View) error { + textarea, err := g.View("textarea") + if err != nil { + log.Panicln(err) + } + message := textarea.Buffer() + msg := string(message) + if msg == "" { + return nil } - // msg, err := bufio.NewReader(os.Stdin).ReadString('\n') - // fmt.Println(msg) + if msg == "" { + return nil + } - //fmt.Println(string(data)) - - //ui() + fmt.Fprint(conn, msg) + textarea.Clear() + textarea.SetCursor(0, 0) + return nil } diff --git a/cmd/ui.go b/cmd/ui.go index 41260db..d3988f6 100644 --- a/cmd/ui.go +++ b/cmd/ui.go @@ -3,10 +3,13 @@ package cmd import ( "fmt" "github.com/jroimartin/gocui" + "github.com/nsf/termbox-go" "log" + "net" + "time" ) -func ui() { +func ui(conn net.Conn) { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { log.Panicln(err) @@ -17,31 +20,30 @@ func ui() { g.Cursor = true initKeybindings(g) + go listener(g, conn) + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } -func quit(*gocui.Gui, *gocui.View) error { - return gocui.ErrQuit -} - -func sendmsg(g *gocui.Gui, v *gocui.View) error { +func listener(g *gocui.Gui, conn net.Conn) { + time.Sleep(time.Second) chatbox, err := g.View("chatbox") - textarea, err := g.View("textarea") if err != nil { log.Panicln(err) } - message := textarea.Buffer() - msg := string(message) - if msg == "" { - return nil - } + for { + reply := make([]byte, 2048) + conn.Read(reply) - fmt.Fprint(chatbox, msg) - textarea.Clear() - textarea.SetCursor(0, 0) - return nil + fmt.Fprintln(chatbox, string(reply)) + termbox.Interrupt() + } +} + +func quit(*gocui.Gui, *gocui.View) error { + return gocui.ErrQuit } func initKeybindings(g *gocui.Gui) error { @@ -67,7 +69,6 @@ func layout(g *gocui.Gui) error { return err } chatbox.Title = "Chat Box" - fmt.Fprintln(chatbox, "Hello world!") } if button, err := g.SetView("button", maxX/2+32, maxY-4, maxX/2+40, maxY-2); err != nil { @@ -99,10 +100,5 @@ func layout(g *gocui.Gui) error { currentUsers.Title = "Connected users" } - // TODO: Add "Users currently online" view - // if currentUsers, err := g.SetView("currentUsers", maxX/2+34, 1, maxX/2+40, maxY/2+11); err != nil { - // - // } - return nil } -- 2.30.2 From c85fa5b03102934fe1172c62af8411fd33c0ec83 Mon Sep 17 00:00:00 2001 From: raul Date: Sun, 14 Apr 2024 09:27:17 +0200 Subject: [PATCH 8/9] Fixed catastrophic memory leak in client Whenever a server would be stopped while the clients were connected to it, the for loop handling the messages received by the server would start a feedback loop as no more data could be read from "conn", thus spawning an infinite number of byte arrays and crashing my laptop --- cmd/ui.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/ui.go b/cmd/ui.go index d3988f6..1c5b7cc 100644 --- a/cmd/ui.go +++ b/cmd/ui.go @@ -35,7 +35,11 @@ func listener(g *gocui.Gui, conn net.Conn) { } for { reply := make([]byte, 2048) - conn.Read(reply) + _, err := conn.Read(reply) + if err != nil { + g.Close() + log.Fatalf("Server closed connection\n") + } fmt.Fprintln(chatbox, string(reply)) termbox.Interrupt() -- 2.30.2 From cec33907b7ea4039532901bc6b2a70b444e3d8c4 Mon Sep 17 00:00:00 2001 From: raul Date: Sun, 14 Apr 2024 09:48:21 +0200 Subject: [PATCH 9/9] Removed currentUsers view I cannot handle this codebase anymore --- cmd/ui.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/ui.go b/cmd/ui.go index 1c5b7cc..9f75b2f 100644 --- a/cmd/ui.go +++ b/cmd/ui.go @@ -97,12 +97,12 @@ func layout(g *gocui.Gui) error { textarea.Editable = true } - if currentUsers, err := g.SetView("currentUsers", maxX/2+42, 1, maxX-6, maxY-6); err != nil { - if err != gocui.ErrUnknownView { - return err - } - currentUsers.Title = "Connected users" - } + // if currentUsers, err := g.SetView("currentUsers", maxX/2+42, 1, maxX-6, maxY-6); err != nil { + // if err != gocui.ErrUnknownView { + // return err + // } + // currentUsers.Title = "Connected users" + // } return nil } -- 2.30.2