How to do it...

These steps cover the process of writing and running your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter5/udp and navigate to this directory.
  2. Run the following command:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter5/udp

You should see a file called go.mod that contains the following:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter5/udp    
  1. Copy tests from ~/projects/go-programming-cookbook-original/chapter5/udp or use this as an exercise to write some of your own code!
  2. Create a new directory named server and navigate to it.
  1. Create a file called broadcast.go with the following content:
package main

import (
"fmt"
"net"
"sync"
"time"
)

type connections struct {
addrs map[string]*net.UDPAddr
// lock for modifying the map
mu sync.Mutex
}

func broadcast(conn *net.UDPConn, conns *connections) {
count := 0
for {
count++
conns.mu.Lock()
// loop over known addresses
for _, retAddr := range conns.addrs {

// send a message to them all
msg := fmt.Sprintf("Sent %d", count)
if _, err := conn.WriteToUDP([]byte(msg), retAddr); err != nil {
fmt.Printf("error encountered: %s", err.Error())
continue
}

}
conns.mu.Unlock()
time.Sleep(1 * time.Second)
}
}
  1. Create a file called main.go with the following content:
package main

import (
"fmt"
"net"
)

const addr = "localhost:8888"

func main() {
conns := &connections{
addrs: make(map[string]*net.UDPAddr),
}

fmt.Printf("serving on %s\n", addr)

// construct a udp addr
addr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
panic(err)
}

// listen on our specified addr
conn, err := net.ListenUDP("udp", addr)
if err != nil {
panic(err)
}
// cleanup
defer conn.Close()

// async send messages to all known clients
go broadcast(conn, conns)

msg := make([]byte, 1024)
for {
// receive a message to gather the ip address
// and port to send back to
_, retAddr, err := conn.ReadFromUDP(msg)
if err != nil {
continue
}

//store it in a map
conns.mu.Lock()
conns.addrs[retAddr.String()] = retAddr
conns.mu.Unlock()
fmt.Printf("%s connected\n", retAddr)
}
}
  1. Navigate to the previous directory.
  2. Create a new directory named client and navigate to it.
  1. Create a file called main.go with the following content:
package main

import (
"fmt"
"net"
)

const addr = "localhost:8888"

func main() {
fmt.Printf("client for server url: %s\n", addr)

addr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
panic(err)
}

conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
panic(err)
}
defer conn.Close()

msg := make([]byte, 512)
n, err := conn.Write([]byte("connected"))
if err != nil {
panic(err)
}
for {
n, err = conn.Read(msg)
if err != nil {
continue
}
fmt.Printf("%s\n", string(msg[:n]))
}
}
  1. Navigate to the previous directory.
  2. Run go run ./server and you will see the following output:
$ go run ./server
serving on localhost:8888
  1. In a separate Terminal, run go run ./client from the udp directory and you will see the following output, although the counts may differ:
$ go run ./client 
client for server url: localhost:8888
Sent 3
Sent 4
Sent 5
  1. Navigate to the Terminal that is running the server and you should see something similar to the following:
$ go run ./server 
serving on localhost:8888
127.0.0.1:64242 connected
  1. Press Ctrl + C to exit both the server and client.
  2. If you copied or wrote your own tests, go up one directory and run go test. Ensure that all the tests pass.