This commit is contained in:
2025-11-02 01:25:30 +01:00
commit ed18be9fbd
17 changed files with 598 additions and 0 deletions

39
network/udp/enums.go Normal file
View File

@@ -0,0 +1,39 @@
package udp
import (
//"encoding/binary"
//"errors"
//"fmt"
"github.com/google/uuid"
)
type PacketType uint8
const (
PacketTypePing PacketType = 0
PacketTypeConnect PacketType = 1
PacketTypeDisconnect PacketType = 2
PacketTypeVoiceData PacketType = 9
)
type Message interface {
Type() PacketType
MarshalBinary() ([]byte, error)
Size() int
}
// MessagePing ClientPing représente un message ping client
type MessagePing struct {
MessageID uuid.UUID
}
func (m *MessagePing) Type() PacketType { return PacketTypePing }
func (m *MessagePing) Size() int { return 17 } // 1 + 16
func (m *MessagePing) MarshalBinary() ([]byte, error) {
buf := make([]byte, m.Size())
buf[0] = byte(PacketTypePing)
copy(buf[1:], m.MessageID[:])
return buf, nil
}

View File

@@ -0,0 +1,55 @@
package udp
import (
"net"
"sync"
mapset "github.com/deckarep/golang-set/v2"
)
type RoutingTable struct {
mu sync.RWMutex
routes map[string]mapset.Set[*net.UDPAddr]
}
func NewRoutingTable() *RoutingTable {
return &RoutingTable{
routes: make(map[string]mapset.Set[*net.UDPAddr]),
}
}
func (rt *RoutingTable) AddClient(channelID string, addr *net.UDPAddr) {
rt.mu.Lock()
defer rt.mu.Unlock()
if rt.routes[channelID] == nil {
rt.routes[channelID] = mapset.NewSet[*net.UDPAddr]()
}
rt.routes[channelID].Add(addr)
}
func (rt *RoutingTable) RemoveClient(channelID string, addr *net.UDPAddr) {
rt.mu.Lock()
defer rt.mu.Unlock()
if clients, exists := rt.routes[channelID]; exists {
clients.Remove(addr)
if clients.Cardinality() == 0 {
delete(rt.routes, channelID)
}
}
}
// GetClients returns the clients connected to the given channelID
// don't modify the returned set!
func (rt *RoutingTable) GetClients(channelID string) mapset.Set[*net.UDPAddr] {
rt.mu.RLock()
defer rt.mu.RUnlock()
clients, exists := rt.routes[channelID]
if !exists {
return nil
}
return clients
}

115
network/udp/server.go Normal file
View File

@@ -0,0 +1,115 @@
package udp
import (
"context"
"fmt"
"net"
"runtime"
"sync"
)
type Server struct {
bindAddr string
routingTable *RoutingTable
conn *net.UDPConn
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
}
func NewServer(bindAddr string) (*Server, error) {
ctx, cancel := context.WithCancel(context.Background())
return &Server{
bindAddr: bindAddr,
ctx: ctx,
cancel: cancel,
}, nil
}
func (s *Server) run() error {
add, err := net.ResolveUDPAddr("udp", s.bindAddr)
if err != nil {
return fmt.Errorf("cannot resolve address: %w", err)
}
s.conn, err = net.ListenUDP("udp", add)
if err != nil {
return fmt.Errorf("cannot listen on address: %w", err)
}
s.conn.SetReadBuffer(8 * 1024 * 1024)
s.conn.SetWriteBuffer(8 * 1024 * 1024)
fmt.Println("Listening on", s.bindAddr)
for i := 0; i < runtime.NumCPU(); i++ {
s.wg.Add(1)
// todo : add so_reuseport option when on unix like system
go s.workerLoop(i)
}
return nil
}
func (s *Server) sendTo(data []byte, addr *net.UDPAddr) error {
if s.conn == nil {
return fmt.Errorf("server not started")
}
_, err := s.conn.WriteToUDP(data, addr)
return err
}
func (s *Server) workerLoop(id int) {
defer s.wg.Done()
buffer := make([]byte, 1500)
fmt.Println("Worker", id, "started")
for {
select {
case <-s.ctx.Done():
fmt.Println("Worker", id, "stopped")
return
default:
size, addr, err := s.conn.ReadFromUDP(buffer)
if err != nil {
if s.ctx.Err() != nil {
return
}
if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() {
continue
}
fmt.Printf("Error reading from UDP: %v\n", err)
continue
}
s.handlePacket(buffer[:size], addr)
}
}
}
func (s *Server) handlePacket(data []byte, addr *net.UDPAddr) {
pt := PacketType(data[0])
switch pt {
case PacketTypePing:
err := s.sendTo([]byte{byte(PacketTypePing)}, addr)
if err != nil {
return
}
return
case PacketTypeConnect:
return
case PacketTypeDisconnect:
return
case PacketTypeVoiceData:
// todo : déterminer le format du packet
//channelID := string(data[1:5])
return
default:
return
}
}