From 58f9bd0aaef309d695dafe1e5d1ddc5c1d43776c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 21 Jul 2018 21:58:05 -0600 Subject: [PATCH] TCP chat works with multiple telnet clients --- README.md | 22 +++++++++++++++++++++- chatserver.go | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a14fe9..a5764de 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ # chat.go -Rudimentary go chat server as a fun project. \ No newline at end of file +Rudimentary go chat server as a fun project. + +# Install + +```bash +git clone https://git.coolaj86.com/coolaj86/chat.go.git +``` + +# Usage + +Start the server + +```bash +go run chatserver.go +``` + +Connect with another client or three. + +```bash +telnet localhost 4080 +``` diff --git a/chatserver.go b/chatserver.go index 9c6e2e5..121a739 100644 --- a/chatserver.go +++ b/chatserver.go @@ -7,8 +7,18 @@ import ( "net" "os" "strconv" + "time" ) +type myMsg struct { + sender net.Conn + bytes []byte + receivedAt time.Time +} + +var myMsgs chan myMsg +var myConns map[net.Conn]bool + func usage() { fmt.Fprintf(os.Stderr, "\nusage: go run chatserver.go\n") flag.PrintDefaults(); @@ -22,6 +32,7 @@ func handleConnection(conn net.Conn) { //buf := make([]byte, 0, 1024) //buf := []byte{} // But this does + fmt.Fprintf(conn, "Welcome! This is an open relay chat server. There is no security yet.\n") buf := make([]byte, 1024) for { count, err := conn.Read(buf) @@ -38,18 +49,24 @@ func handleConnection(conn net.Conn) { // fmt.Fprintf(os.Stdout, "Weird") continue } - fmt.Fprintf(os.Stdout, "Message:\n%s\n", buf[0:count]) + myMsgs <- myMsg{ + receivedAt: time.Now(), + sender: conn, + bytes: buf[0:count], + } } } func main() { + myMsgs = make(chan myMsg, 128) + myConns = make(map[net.Conn]bool) flag.Usage = usage port:= flag.Uint("telnet-port", 4080, "tcp telnet chat port") flag.Parse() addr := ":" + strconv.Itoa(int(*port)) - // https://golang.org/pkg/net/ + // https://golang.org/pkg/net/#Conn sock, err := net.Listen("tcp", addr) if nil != err { fmt.Fprintf(os.Stderr, "Couldn't bind to TCP socket %q: %s\n", addr, err) @@ -57,6 +74,30 @@ func main() { } fmt.Println("Listening on", addr); + go func() { + for { + fmt.Fprintf(os.Stdout, "Waiting for message...\n"); + msg := <- myMsgs + ts, err := msg.receivedAt.MarshalJSON() + if nil != err { + fmt.Fprintf(os.Stderr, "[Error] %s\n", err) + } + fmt.Fprintf(os.Stdout, "[Timestamp] %s\n", ts) + fmt.Fprintf(os.Stdout, "[Remote] %s\n", msg.sender.RemoteAddr().String()) + fmt.Fprintf(os.Stdout, "[Message] %s\n", msg.bytes); + for conn, _ := range myConns { + if msg.sender == conn { + continue + } + // backlogged connections could prevent a next write, + // so this should be refactored into a goroutine + // And what to do about slow clients that get behind (or DoS)? + // SetDeadTime and Disconnect them? + conn.Write(msg.bytes) + } + } + }() + for { conn, err := sock.Accept() if err != nil { @@ -65,6 +106,7 @@ func main() { fmt.Fprintf(os.Stderr, "Error accepting connection:\n%s\n", err) } fmt.Fprintf(os.Stdout, "Accepting socket\n") + myConns[conn] = true go handleConnection(conn) } }