Skip to content

Instantly share code, notes, and snippets.

@antonmry
Created June 1, 2016 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antonmry/886d8f004db799483e3431be00d3902b to your computer and use it in GitHub Desktop.
Save antonmry/886d8f004db799483e3431be00d3902b to your computer and use it in GitHub Desktop.
dailyprogrammer: Network and Cards: Part 1, The network
package main
// See https://www.reddit.com/r/dailyprogrammer/comments/4knivr/20160523_challenge_268_easy_network_and_cards/
import (
"flag"
"fmt"
"net"
"os"
"sync"
"time"
"strings"
"strconv"
"os/signal"
"syscall"
"errors"
)
const (
bufferSize = 1024
)
type clientRecord struct {
name string
address string
port int
}
var registeredClientsStorage map[clientRecord]time.Time
var registeredClientsMutex sync.RWMutex
func main() {
isServerPtr := flag.Bool("server", false, "Cards will start as server")
flag.Parse()
err := validateParams(*isServerPtr, flag.NArg())
checkError(err)
if *isServerPtr {
server()
} else {
client(strings.Join(flag.Args(), " "))
}
}
func validateParams(isServer bool, NArg int) error {
if isServer && NArg > 1 {
return errors.New("Server option doesn't accept more parameters")
}
if !isServer && NArg < 1 {
return errors.New("Please, add at least a message to send to the server\n")
}
return nil
}
func client(clientName string) {
ServerAddress, err := net.ResolveUDPAddr("udp", "127.0.0.1:10001")
checkError(err)
LocalAddress, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
checkError(err)
Conn, err := net.DialUDP("udp", LocalAddress, ServerAddress)
checkError(err)
defer Conn.Close()
bufSent := []byte("REGISTER " + clientName)
n, err := Conn.Write(bufSent)
if err != nil {
fmt.Printf("Client: error sending: %s\n", err)
}
fmt.Printf("Client: sent registration: %d bytes\n", n)
bufReceived := make([]byte, bufferSize)
for {
n, _, err := Conn.ReadFromUDP(bufReceived)
if err != nil {
fmt.Printf("Client: error receiving: %s\n", err)
}
fmt.Printf("Client: received %s\n", string(bufReceived[0:n]))
}
}
func server() {
registeredClientsStorage = make(map[clientRecord]time.Time)
registeredClientsMutex = sync.RWMutex{}
go signalCatcher()
fmt.Printf("Server: execute kill -SIGUSR1 %d\n", os.Getpid())
ServerAddress, err := net.ResolveUDPAddr("udp", ":10001")
checkError(err)
ServerConn, err := net.ListenUDP("udp", ServerAddress)
checkError(err)
defer ServerConn.Close()
// Keepalives
t := time.NewTicker(5 * time.Second)
go func() {
for {
keepalives(*ServerConn)
<-t.C
}
}()
// Process messages
buf := make([]byte, bufferSize)
for {
n, addr, err := ServerConn.ReadFromUDP(buf)
if err != nil {
fmt.Printf("Server: error receiving: %s\n", err)
}
if strings.HasPrefix(string(buf[0:n]), "REGISTER") {
go func(addr *net.UDPAddr, n int) {
var clientName = ""
clientName = string(buf[9:n])
newClient := clientRecord{clientName, addr.IP.String(), addr.Port}
registeredClientsMutex.Lock()
registeredClientsStorage[newClient] = time.Now()
registeredClientsMutex.Unlock()
fmt.Printf("Server: client %s from %s registered\n", clientName, addr)
} (addr, n)
} else {
fmt.Printf("Server: gnored message: %s from %s\n", string(buf[0:n]), addr)
}
}
}
func keepalives(ServerConn net.UDPConn) {
// NOTE: here we are accessing the map in an unsafe way!
for k := range registeredClientsStorage {
go func(k clientRecord) {
fmt.Printf("Server: keep alive to %s at %s:%d\n", k.name, k.address, k.port)
ClientAddress, err := net.ResolveUDPAddr("udp", k.address + ":" + strconv.Itoa(k.port))
checkError(err)
buf := []byte("KEEPALIVE 127.0.0.1:10001")
_, err = ServerConn.WriteToUDP(buf, ClientAddress)
if err != nil {
fmt.Printf("Server: error sending keepalive: %s\n", err)
}
}(k)
}
}
func signalCatcher() {
for {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGUSR1)
<-ch
fmt.Println("Server: listing clients")
for k := range registeredClientsStorage {
fmt.Printf("Server: client %s at %s:%d\n", k.name, k.address, k.port)
}
}
}
// Helpers
func checkError(err error) {
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(-1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment