Created
December 8, 2023 20:49
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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