mirror of
https://github.com/luscis/openlan.git
synced 2025-10-05 16:47:11 +08:00
217 lines
4.0 KiB
Go
Executable File
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()
|
|
}
|