Files
gobngsdk/udp_transport.go
T
esilva 312d979be1 added some new API calls
mainly around getting the file size and data read for convenience for
other tools
2026-09-16 23:47:35 +01:00

147 lines
2.6 KiB
Go

package bngsdk
import (
"errors"
"log/slog"
"net"
"sync"
"time"
"unsafe"
)
// ErrReaderDisabled = errors.New("reader is currently turned off")
var (
ErrNoData = errors.New("no new data available")
readTimeout = (time.Second / 60) * 5 // N missed frames at 60fps
packetPool = sync.Pool{
New: func() any {
var b packetBuffer
return &b
},
}
)
type packetBuffer [unsafe.Sizeof(Outgauge{})]byte
type frame struct {
Buf *packetBuffer
Len int
}
type UDPTransport struct {
address *net.UDPAddr
connection *net.UDPConn
dataChan chan frame
totalBytes int64 // returns total bytes read in case of reader and written in case of writer
}
func NewUDPReader(ip string, port int) (*UDPTransport, error) {
// Define the IP address and port to listen on
addr := &net.UDPAddr{
IP: net.ParseIP(ip),
Port: port,
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return nil, err
}
udpT := UDPTransport{
address: addr,
connection: conn,
dataChan: make(chan frame, 1),
}
go udpT.udpSink()
return &udpT, nil
}
func NewUDPWriter(ip string, port int) (*UDPTransport, error) {
// Define the IP address and port to listen on
addr := &net.UDPAddr{
IP: net.ParseIP(ip),
Port: port,
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
return nil, err
}
return &UDPTransport{
address: addr,
connection: conn,
}, nil
}
// udpSink is a loop that will consume the most recent packets on the port
// instead of letting them pile up
func (ut *UDPTransport) udpSink() {
defer close(ut.dataChan)
for {
ut.connection.SetReadDeadline(time.Now().Add(readTimeout))
bufPtr := packetPool.Get().(*packetBuffer)
nBytes, _, err := ut.connection.ReadFromUDP(bufPtr[:])
if err != nil {
return
}
frame := frame{
Buf: bufPtr,
Len: nBytes,
}
select {
case ut.dataChan <- frame:
// Packet sent successfuly
default:
select {
case oldFrame := <-ut.dataChan:
packetPool.Put(oldFrame.Buf)
default:
}
ut.dataChan <- frame
}
}
}
func (ut *UDPTransport) Write(data []byte) (int, error) {
nBytes, err := ut.connection.Write(data)
ut.totalBytes += int64(nBytes)
return nBytes, err
}
func (ut *UDPTransport) Read(buffer []byte) (int, error) {
latestFrame, ok := <-ut.dataChan
if !ok {
slog.Error(ErrNoData.Error())
return 0, ErrNoData
}
nBytes := copy(buffer, latestFrame.Buf[:latestFrame.Len])
packetPool.Put(latestFrame.Buf)
ut.totalBytes += int64(nBytes)
return nBytes, nil
}
func (ut *UDPTransport) Close() error {
if ut.connection != nil {
return ut.connection.Close()
}
return nil
}
func (ut *UDPTransport) GetTotalBytes() int64 {
return ut.totalBytes
}