Skip to content

Instantly share code, notes, and snippets.

@nebuxadnezzar
Created December 8, 2023 20:49
Show Gist options
  • Save nebuxadnezzar/58eb71e48407064314a2c64af6724118 to your computer and use it in GitHub Desktop.
Save nebuxadnezzar/58eb71e48407064314a2c64af6724118 to your computer and use it in GitHub Desktop.
UDP listener and more... Demonstrates UDP echo server, generics, flag command line argument usage, range parsing.
package main
// go run udp-listener-with-interrupt-chan.go -p 8008:8009
// echo hello kosmos | nc -u -v -w 1 0.0.0.0 8009
import (
"fmt"
"net"
"os"
"os/signal"
"syscall"
"flag"
"strings"
"regexp"
"strconv"
)
const DEFAULT_PORT = `8008`
type config struct {
ports []string
}
type Number interface {
int | int64 | float64
}
func main() {
cf := makeconfig(os.Args)
run(cf)
}
func run(cf *config) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
done := make(chan bool, 1)
defer close(done)
go func() {
sig := <-sigs
fmt.Println(sig)
done <- true
}()
outchan := make(chan []byte, 1)
errchan := make(chan error, 1)
go listen(outchan, errchan, cf)
fmt.Println("awaiting signal")
loop := true
for loop {
select {
case <-done:
loop = false
case err := <- errchan:
fmt.Printf("ERR: %v\n", err)
loop = false
case data := <- outchan:
fmt.Printf("---> %s\n", string(data))
}
}
fmt.Println("exiting")
}
func listen(outchan chan []byte, errchan chan error, cf *config) {
defer close(errchan)
defer close(outchan)
var pc net.PacketConn
var err error
for _, port := range cf.ports {
fmt.Printf("trying to connect to %s\n", port)
pc, err = net.ListenPacket("udp4", ":" + port)
if err == nil { break}
}
if err != nil {
errchan <- err
return
}
fmt.Printf("Listening on %s\n", pc.LocalAddr().String())
defer pc.Close()
buf := make([]byte, 1024)
for {
n, addr, err := pc.ReadFrom(buf)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
break
}
outchan <- []byte(fmt.Sprintf("%s sent %d bytes: %s", addr, n, buf[:n]))
}
}
func makeconfig(args []string) *config {
cf := &config{}
flag.Usage = func() { // [1]
fmt.Fprintf(flag.CommandLine.Output(), "usage: %s [-p]\n", args[0])
flag.PrintDefaults()
}
var ps string
flag.StringVar(&ps, "port", DEFAULT_PORT, "port range to connect to -port num:num")
flag.StringVar(&ps, "p", DEFAULT_PORT, "port range to connect to -p num:num")
flag.CommandLine.Parse(args[1:])
ports := StrRangeToIntArray(ps, ":")
cf.ports = make([]string, 0, len(ports))
for _, v := range ports {cf.ports = append(cf.ports, strconv.Itoa(v))}
return cf
}
func StrRangeToIntArray(s string, sep string) []int {
rx := regexp.MustCompile(`^\d+$`)
ia := make([]int, 0, 3)
minmax := make([]int, 0, 3)
a := strings.Split(s, sep)
for _, v := range a {
if rx.MatchString(v) {
if n, e := strconv.Atoi(v); e == nil {minmax = append(minmax, n)}
}
}
for i, k := Min(minmax ...), Max(minmax...); i <= k; i++ {
ia = append(ia, i)
}
return ia
}
func Max[V Number](nums ...V) V {
var mx V = V(nums[0])
for _, val := range nums {
if val > mx { mx = val}
}
return mx
}
func Min[V Number](nums ...V) V {
var mn V = V(nums[0])
for _, val := range nums {
if val < mn { mn = val}
}
return mn
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment