eq2go/internal/udp/server.go

106 lines
1.8 KiB
Go

package udp
import (
"fmt"
"net"
"sync"
"time"
)
type Server struct {
conn *net.UDPConn
connections map[string]*Connection
mutex sync.RWMutex
handler PacketHandler
running bool
}
type PacketHandler interface {
HandlePacket(conn *Connection, packet *ApplicationPacket)
}
func NewServer(addr string, handler PacketHandler) (*Server, error) {
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
return nil, err
}
return &Server{
conn: conn,
connections: make(map[string]*Connection),
handler: handler,
}, nil
}
func (s *Server) Start() error {
s.running = true
// Start connection timeout checker
go s.timeoutChecker()
// Main packet receive loop
buffer := make([]byte, 2048)
for s.running {
n, addr, err := s.conn.ReadFromUDP(buffer)
if err != nil {
if s.running {
fmt.Printf("UDP read error: %v\n", err)
}
continue
}
go s.handlePacket(buffer[:n], addr)
}
return nil
}
func (s *Server) Stop() {
s.running = false
s.conn.Close()
}
func (s *Server) handlePacket(data []byte, addr *net.UDPAddr) {
if len(data) < 2 {
return
}
connKey := addr.String()
s.mutex.Lock()
conn, exists := s.connections[connKey]
if !exists {
conn = NewConnection(addr, s.conn, s.handler)
s.connections[connKey] = conn
}
s.mutex.Unlock()
conn.ProcessPacket(data)
}
func (s *Server) timeoutChecker() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
if !s.running {
return
}
now := time.Now()
s.mutex.Lock()
for key, conn := range s.connections {
if now.Sub(conn.lastPacketTime) > 45*time.Second {
conn.Close()
delete(s.connections, key)
}
}
s.mutex.Unlock()
}
}