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