package artnet import ( "context" "net" "syscall" "time" ) type Handler interface { HandleDMX(src *net.UDPAddr, pkt *DMXPacket) HandlePoll(src *net.UDPAddr, pkt *PollPacket) HandlePollReply(src *net.UDPAddr, pkt *PollReplyPacket) } type Receiver struct { conn *net.UDPConn handler Handler done chan struct{} } func NewReceiver(addr *net.UDPAddr, handler Handler) (*Receiver, error) { conn, err := net.ListenUDP("udp4", addr) if err != nil { return nil, err } return &Receiver{ conn: conn, handler: handler, done: make(chan struct{}), }, nil } func NewDefaultReceiver(handler Handler) (*Receiver, error) { return NewReceiver(&net.UDPAddr{Port: Port}, handler) } func NewInterfaceReceiver(ifaceName string, handler Handler) (*Receiver, error) { lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { var err error c.Control(func(fd uintptr) { err = syscall.SetsockoptString(int(fd), syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, ifaceName) }) return err }, } conn, err := lc.ListenPacket(context.Background(), "udp4", ":6454") if err != nil { return nil, err } return &Receiver{ conn: conn.(*net.UDPConn), handler: handler, done: make(chan struct{}), }, nil } func (r *Receiver) Start() { go r.loop() } func (r *Receiver) Stop() { close(r.done) r.conn.Close() } func (r *Receiver) Conn() *net.UDPConn { return r.conn } func (r *Receiver) LocalAddr() net.Addr { return r.conn.LocalAddr() } func (r *Receiver) SendTo(data []byte, addr *net.UDPAddr) error { _, err := r.conn.WriteToUDP(data, addr) return err } func (r *Receiver) loop() { buf := make([]byte, 1024) for { select { case <-r.done: return default: } r.conn.SetReadDeadline(time.Now().Add(1 * time.Second)) n, src, err := r.conn.ReadFromUDP(buf) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { continue } select { case <-r.done: return default: continue } } r.handle(src, buf[:n]) } } func (r *Receiver) handle(src *net.UDPAddr, data []byte) { opCode, pkt, err := ParsePacket(data) if err != nil { return } switch opCode { case OpDmx: if dmx, ok := pkt.(*DMXPacket); ok { r.handler.HandleDMX(src, dmx) } case OpPoll: if poll, ok := pkt.(*PollPacket); ok { r.handler.HandlePoll(src, poll) } case OpPollReply: if reply, ok := pkt.(*PollReplyPacket); ok { r.handler.HandlePollReply(src, reply) } } }