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

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
}
}