How to write the simplest chat server in golang

Introduction

In this tutorial, we implement a very simple chat server with golang that uses golang standard libraries. All we need is a broadcaster method that sends messages to clients and a handler method for handling the connection.

func main() {
listen, err := net.Listen("tcp", "localhost:8080")
if err != nil {
return
}
}
for {
conn, err := listen.Accept()
if err != nil {
log.Print(err)
continue
}
}

Handler

Now we must handle a connection on the chat server. We create a map of client address and connection and broadcast a message that notices every one a client joined after that we must broadcast every single message of the client to other clients at the end we broadcast a message to everyone if any client closes its connection.
So we need a map, two channels, and a message struct:

var clients = make(map[string]net.Conn)
var leaving = make(chan message)
var messages = make(chan message)

type message struct {
text string
address string
}
func handle(conn net.Conn) {
clients[conn.RemoteAddr().String()] = conn

messages <- newMessage(" joined.", conn)

input := bufio.NewScanner(conn)
for input.Scan() {
messages <- newMessage(": "+input.Text(), conn)
}
//Delete client form map
delete(clients, conn.RemoteAddr().String())

leaving <- newMessage(" has left.", conn)

conn.Close() // ignore errors
}
func newMessage(msg string, conn net.Conn) message {
addr := conn.RemoteAddr().String()
return message{
text: addr + msg,
address: addr,
}
}

Broadcaster

So we need a broadcaster to broadcast all messages to all clients we can use select we heave two cases one for all messages and the other one is for leaving messages they listen to channels iterate on our client map skip the message producer and write a message on connection

func broadcaster() {
for {
select {
case msg := <-messages:
for _, conn := range clients {
if msg.address == conn.RemoteAddr().String() {
continue
}
fmt.Fprintln(conn, msg.text) // NOTE: ignoring network errors
}

case msg := <-leaving:
for _, conn := range clients {
fmt.Fprintln(conn, msg.text) // NOTE: ignoring network errors
}

}
}
}
func main() {
listen, err := net.Listen("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}

go broadcaster()
for {
conn, err := listen.Accept()
if err != nil {
log.Print(err)
continue
}
go handle(conn)
}
}
package main

import (
"io"
"log"
"net"
"os"
)

func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, conn) // NOTE: ignoring errors
log.Println("done")
done <- struct{}{} // signal the main goroutine
}()
mustCopy(conn, os.Stdin)
conn.Close()
<-done // wait for background goroutine to finish
}

func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatal(err)
}
}

//todo add bio