Files
zeromesh/internal/vl1/transport.go

113 lines
2.3 KiB
Go

package vl1
import (
"fmt"
"log/slog"
"net"
"sync"
"syscall"
"time"
)
type Transport struct {
conn *net.UDPConn
port int
mu sync.RWMutex
closed bool
log *slog.Logger
}
func NewTransport(port int, log *slog.Logger) (*Transport, error) {
addr := &net.UDPAddr{Port: port}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return nil, fmt.Errorf("bind UDP port %d: %w", port, err)
}
actualPort := conn.LocalAddr().(*net.UDPAddr).Port
log.Info("VL1 transport listening", "port", actualPort)
return &Transport{
conn: conn,
port: actualPort,
log: log,
}, nil
}
func (t *Transport) Port() int {
return t.port
}
func (t *Transport) ReadFrom(buf []byte) (int, *net.UDPAddr, error) {
n, addr, err := t.conn.ReadFromUDP(buf)
return n, addr, err
}
func (t *Transport) SendTo(data []byte, addr *net.UDPAddr) error {
t.mu.RLock()
defer t.mu.RUnlock()
if t.closed {
return fmt.Errorf("transport closed")
}
_, err := t.conn.WriteToUDP(data, addr)
return err
}
func (t *Transport) SendPacket(pkt *Packet, addr *net.UDPAddr) error {
return t.SendTo(pkt.Encode(), addr)
}
func (t *Transport) Close() error {
t.mu.Lock()
defer t.mu.Unlock()
t.closed = true
return t.conn.Close()
}
func (t *Transport) SetSocketBuffers(rcvBuf, sndBuf int) error {
rawConn, err := t.conn.SyscallConn()
if err != nil {
return fmt.Errorf("get raw conn: %w", err)
}
var setErr error
err = rawConn.Control(func(fd uintptr) {
if rcvBuf > 0 {
if e := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, rcvBuf); e != nil {
setErr = fmt.Errorf("set SO_RCVBUF=%d: %w", rcvBuf, e)
return
}
}
if sndBuf > 0 {
if e := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, sndBuf); e != nil {
setErr = fmt.Errorf("set SO_SNDBUF=%d: %w", sndBuf, e)
return
}
}
})
if err != nil {
return err
}
return setErr
}
func (t *Transport) LocalAddr() net.Addr {
return t.conn.LocalAddr()
}
func (t *Transport) SetReadDeadline(deadline time.Time) error {
return t.conn.SetReadDeadline(deadline)
}
var packetBufPool = sync.Pool{
New: func() interface{} {
buf := make([]byte, MaxPacketSize)
return &buf
},
}
func GetPacketBuf() *[]byte {
return packetBufPool.Get().(*[]byte)
}
func PutPacketBuf(buf *[]byte) {
packetBufPool.Put(buf)
}