package udp import ( "errors" "net" "time" "src.dualinventive.com/go/lib/dilog" ) //DefaultUDPWriteTimeoutSeconds is the default time a udp write operation will block const DefaultUDPWriteTimeoutSeconds = 1 //DefaultUDPBufferSize is the default buffer size a udp read operation const DefaultUDPBufferSize = 4096 //ErrNilLogger is returned when no logger is provided var ErrNilLogger = errors.New("logger must not be nil") //ErrNilHandler is return when no handler is provided var ErrNilHandler = errors.New("handler must not be nil") //Listener listens to udp and routes them to its handler type Listener struct { log dilog.Logger pc net.PacketConn buffer []byte handler PacketHandler } //PacketHandler is able to process UDP packets type PacketHandler func([]byte) //NewListener returns a new UDP listener func NewListener( log dilog.Logger, addr string, bufferSize int, writeDeadline int, h PacketHandler, ) (*Listener, error) { if log == nil { return nil, ErrNilLogger } if h == nil { return nil, ErrNilHandler } if addr == "" { log.Warning("udp port not configured, using random port") } if bufferSize == 0 { bufferSize = DefaultUDPBufferSize log.Warning("udp buffer size not configured, defaulting to 1064 bytes") } if writeDeadline == 0 { writeDeadline = DefaultUDPWriteTimeoutSeconds log.Warning("udp write timeout not configured, defaulting to 2 seconds") } pc, err := net.ListenPacket("udp", addr) if err != nil { return nil, err } deadline := time.Now().Add(time.Second * time.Duration(writeDeadline)) err = pc.SetWriteDeadline(deadline) if err != nil { return nil, err } buffer := make([]byte, bufferSize) return &Listener{ log: log, pc: pc, buffer: buffer, handler: h, }, nil } //ListenAndServe starts the active listening on udp. This call is blocking. func (l *Listener) ListenAndServe() error { for { n, _, err := l.pc.ReadFrom(l.buffer) if err != nil { l.log.WithError(err).Warning("error reading udp packet") continue } go l.handler(l.buffer[:n]) } } //LocalAddr returns the local udp address on which is listened. func (l *Listener) LocalAddr() string { return l.pc.LocalAddr().String() }