Files
openlan/pkg/libol/xdp.go
2022-07-29 23:38:54 +08:00

217 lines
4.0 KiB
Go
Executable File

package libol
import (
"net"
"sync"
"time"
)
type XDP struct {
lock sync.RWMutex
bufSize int
connection *net.UDPConn
address *net.UDPAddr
sessions *SafeStrMap
accept chan *XDPConn
}
func XDPListen(addr string, clients, bufSize int) (net.Listener, error) {
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
if bufSize == 0 {
bufSize = MaxBuf
}
Debug("bufSize: %d", bufSize)
x := &XDP{
address: udpAddr,
sessions: NewSafeStrMap(clients),
accept: make(chan *XDPConn, 2),
bufSize: bufSize,
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
return nil, err
}
x.connection = conn
Go(x.Loop)
return x, nil
}
func (x *XDP) Recv(udpAddr *net.UDPAddr, data []byte) error {
// dispatch to XDPConn and new accept
addr := udpAddr.String()
if obj, ok := x.sessions.GetEx(addr); ok {
conn := obj.(*XDPConn)
conn.toQueue(data)
return nil
}
conn := &XDPConn{
connection: x.connection,
remoteAddr: udpAddr,
localAddr: x.address,
readQueue: make(chan []byte, 1024),
closed: false,
onClose: func(conn *XDPConn) {
Info("XDP.Recv: onClose %s", conn)
x.sessions.Del(addr)
},
}
if err := x.sessions.Set(addr, conn); err != nil {
return NewErr("session.Set: %s", err)
}
x.accept <- conn
conn.toQueue(data)
return nil
}
// Loop forever
func (x *XDP) Loop() {
for {
data := make([]byte, x.bufSize)
n, udpAddr, err := x.connection.ReadFromUDP(data)
if err != nil {
Error("XDP.Loop %s", err)
break
}
if err := x.Recv(udpAddr, data[:n]); err != nil {
Warn("XDP.Loop: %s", err)
}
}
}
// Accept waits for and returns the next connection to the listener.
func (x *XDP) Accept() (net.Conn, error) {
return <-x.accept, nil
}
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
func (x *XDP) Close() error {
x.lock.Lock()
defer x.lock.Unlock()
_ = x.connection.Close()
return nil
}
// returns the listener's network address.
func (x *XDP) Addr() net.Addr {
return x.address
}
type XDPConn struct {
lock sync.RWMutex
connection *net.UDPConn
remoteAddr *net.UDPAddr
localAddr *net.UDPAddr
readQueue chan []byte
closed bool
readDead time.Time
writeDead time.Time
onClose func(conn *XDPConn)
}
func (c *XDPConn) toQueue(b []byte) {
c.lock.RLock()
if c.closed {
c.lock.RUnlock()
return
} else {
c.lock.RUnlock()
}
c.readQueue <- b
}
func (c *XDPConn) Read(b []byte) (n int, err error) {
c.lock.RLock()
if c.closed {
c.lock.RUnlock()
return 0, NewErr("read on closed")
}
var timeout *time.Timer
outChan := make(<-chan time.Time)
if !c.readDead.IsZero() {
if time.Now().After(c.readDead) {
c.lock.RUnlock()
return 0, NewErr("read timeout")
}
delay := c.readDead.Sub(time.Now())
timeout = time.NewTimer(delay)
outChan = timeout.C
}
c.lock.RUnlock()
// wait for read event or timeout or error
select {
case <-outChan:
return 0, NewErr("read timeout")
case d := <-c.readQueue:
if timeout != nil {
timeout.Stop()
}
return copy(b, d), nil
}
}
func (c *XDPConn) Write(b []byte) (n int, err error) {
c.lock.RLock()
if c.closed {
c.lock.RUnlock()
return 0, NewErr("write to closed")
} else {
c.lock.RUnlock()
}
return c.connection.WriteToUDP(b, c.remoteAddr)
}
func (c *XDPConn) Close() error {
c.lock.Lock()
defer c.lock.Unlock()
if c.closed {
return nil
}
if c.onClose != nil {
c.onClose(c)
}
c.connection = nil
c.closed = true
return nil
}
func (c *XDPConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *XDPConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *XDPConn) SetDeadline(t time.Time) error {
c.lock.Lock()
defer c.lock.Unlock()
c.readDead = t
c.writeDead = t
return nil
}
func (c *XDPConn) SetReadDeadline(t time.Time) error {
c.lock.Lock()
defer c.lock.Unlock()
c.readDead = t
return nil
}
func (c *XDPConn) SetWriteDeadline(t time.Time) error {
c.lock.Lock()
defer c.lock.Unlock()
c.writeDead = t
return nil
}
func (c *XDPConn) String() string {
return c.remoteAddr.String()
}