Compare commits

..

2 Commits

Author SHA1 Message Date
8acfbf1215 init 2025-11-14 00:34:28 +01:00
340d1b69f9 init 2025-11-11 23:08:29 +01:00
5 changed files with 162 additions and 24 deletions

View File

@@ -4,16 +4,23 @@ import (
"context" "context"
"fmt" "fmt"
"go_oxspeak_server/config" "go_oxspeak_server/config"
"go_oxspeak_server/database"
"go_oxspeak_server/models"
"go_oxspeak_server/network/http" "go_oxspeak_server/network/http"
"go_oxspeak_server/network/udp" "go_oxspeak_server/network/udp"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"gorm.io/gorm"
) )
type App struct { type App struct {
cfg *config.Config cfg *config.Config
// DB
db *gorm.DB
// Serveurs // Serveurs
udpServer *udp.Server udpServer *udp.Server
httpServer *http.Server httpServer *http.Server
@@ -54,7 +61,25 @@ func (app *App) Run() error {
// Context pour gérer l'arrêt gracieux // Context pour gérer l'arrêt gracieux
defer app.cancel() defer app.cancel()
// Lancer les app ici dbConfig := database.DBConfig{
Driver: app.cfg.Database.Type,
DSN: app.cfg.GetDSN(),
}
// 1) Initialiser la DB
if err := database.Initialize(dbConfig); err != nil {
return fmt.Errorf("failed to initialize database: %w", err)
}
// (optionnel) garder une référence locale si tu veux utiliser app.db ailleurs
app.db = database.DB
// 2) Lancer les migrations en utilisant le registry des modèles
if err := database.AutoMigrate(models.All()...); err != nil {
return fmt.Errorf("failed to auto-migrate database: %w", err)
}
// 3) Lancer les workers / serveurs
go app.runWorkers() go app.runWorkers()
fmt.Println("App started, press CTRL+C to stop...") fmt.Println("App started, press CTRL+C to stop...")
@@ -69,6 +94,10 @@ func (app *App) Run() error {
} }
} }
func (app *App) runDBMigrations() {
database.AutoMigrate()
}
func (app *App) runWorkers() { func (app *App) runWorkers() {
// lancer le serveur udp // lancer le serveur udp
go func() { go func() {

14
models/registry.go Normal file
View File

@@ -0,0 +1,14 @@
package models
func All() []interface{} {
return []interface{}{
&User{},
&Server{},
&ServerUser{},
&Category{},
&Channel{},
&ChannelUser{},
&Message{},
&Attachment{},
}
}

View File

@@ -0,0 +1,68 @@
//go:build linux || darwin
// +build linux darwin
package udp
import (
"fmt"
"net"
"golang.org/x/sys/unix"
)
func listenUDP(bindAddr string, workerCount int) ([]*net.UDPConn, error) {
addr, err := net.ResolveUDPAddr("udp", bindAddr)
if err != nil {
return nil, fmt.Errorf("cannot resolve address: %w", err)
}
conns := make([]*net.UDPConn, 0, workerCount)
for i := 0; i < workerCount; i++ {
conn, err := net.ListenUDP("udp", addr)
if err != nil {
// En cas derreur, on ferme ce quon a ouvert.
for _, c := range conns {
_ = c.Close()
}
return nil, fmt.Errorf("cannot listen on address (worker %d): %w", i, err)
}
rawConn, err := conn.SyscallConn()
if err != nil {
_ = conn.Close()
for _, c := range conns {
_ = c.Close()
}
return nil, fmt.Errorf("cannot get raw connection (worker %d): %w", i, err)
}
var sockErr error
err = rawConn.Control(func(fd uintptr) {
sockErr = unix.SetsockoptInt(
int(fd),
unix.SOL_SOCKET,
unix.SO_REUSEPORT,
1,
)
})
if err != nil {
_ = conn.Close()
for _, c := range conns {
_ = c.Close()
}
return nil, fmt.Errorf("control error (worker %d): %w", i, err)
}
if sockErr != nil {
_ = conn.Close()
for _, c := range conns {
_ = c.Close()
}
return nil, fmt.Errorf("cannot set SO_REUSEPORT (worker %d): %w", i, sockErr)
}
conns = append(conns, conn)
}
return conns, nil
}

View File

@@ -0,0 +1,24 @@
//go:build windows
// +build windows
package udp
import (
"fmt"
"net"
)
func listenUDP(bindAddr string, workerCount int) ([]*net.UDPConn, error) {
addr, err := net.ResolveUDPAddr("udp", bindAddr)
if err != nil {
return nil, fmt.Errorf("cannot resolve address: %w", err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return nil, fmt.Errorf("cannot listen on address: %w", err)
}
// Un seul conn partagé par tous les workers.
return []*net.UDPConn{conn}, nil
}

View File

@@ -12,7 +12,7 @@ type Server struct {
bindAddr string bindAddr string
routingTable *RoutingTable routingTable *RoutingTable
conn *net.UDPConn conns []*net.UDPConn
wg sync.WaitGroup wg sync.WaitGroup
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
@@ -29,39 +29,43 @@ func NewServer(bindAddr string) *Server {
} }
func (s *Server) Run() error { func (s *Server) Run() error {
add, err := net.ResolveUDPAddr("udp", s.bindAddr) workerCount := runtime.NumCPU()
if err != nil {
return fmt.Errorf("cannot resolve address: %w", err)
}
s.conn, err = net.ListenUDP("udp", add) conns, err := listenUDP(s.bindAddr, workerCount)
if err != nil { if err != nil {
return fmt.Errorf("cannot listen on address: %w", err) return fmt.Errorf("cannot listen on address: %w", err)
} }
if len(conns) == 0 {
return fmt.Errorf("no UDP connections created")
}
s.conns = conns
for _, conn := range s.conns {
conn.SetReadBuffer(8 * 1024 * 1024)
conn.SetWriteBuffer(8 * 1024 * 1024)
}
s.conn.SetReadBuffer(8 * 1024 * 1024)
s.conn.SetWriteBuffer(8 * 1024 * 1024)
fmt.Println("Listening on", s.bindAddr) fmt.Println("Listening on", s.bindAddr)
for i := 0; i < runtime.NumCPU(); i++ { for i, conn := range s.conns {
s.wg.Add(1) s.wg.Add(1)
// todo : add so_reuseport option when on unix like system go s.workerLoop(i, conn)
go s.workerLoop(i)
} }
return nil return nil
} }
func (s *Server) sendTo(data []byte, addr *net.UDPAddr) error { func (s *Server) sendTo(data []byte, addr *net.UDPAddr) error {
if s.conn == nil { if len(s.conns) == 0 || s.conns[0] == nil {
return fmt.Errorf("server not started") return fmt.Errorf("server not started")
} }
_, err := s.conn.WriteToUDP(data, addr) // On utilise la première conn pour lenvoi (cest suffisant pour UDP).
_, err := s.conns[0].WriteToUDP(data, addr)
return err return err
} }
func (s *Server) workerLoop(id int) { func (s *Server) workerLoop(id int, conn *net.UDPConn) {
defer s.wg.Done() defer s.wg.Done()
buffer := make([]byte, 1500) buffer := make([]byte, 1500)
@@ -73,7 +77,7 @@ func (s *Server) workerLoop(id int) {
fmt.Println("Worker", id, "stopped") fmt.Println("Worker", id, "stopped")
return return
default: default:
size, addr, err := s.conn.ReadFromUDP(buffer) size, addr, err := conn.ReadFromUDP(buffer)
if err != nil { if err != nil {
if s.ctx.Err() != nil { if s.ctx.Err() != nil {
return return
@@ -81,7 +85,7 @@ func (s *Server) workerLoop(id int) {
if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() { if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() {
continue continue
} }
fmt.Printf("Error reading from UDP: %v\n", err) fmt.Printf("Error reading from UDP (worker %d): %v\n", id, err)
continue continue
} }
@@ -91,13 +95,14 @@ func (s *Server) workerLoop(id int) {
} }
func (s *Server) handlePacket(data []byte, addr *net.UDPAddr) { func (s *Server) handlePacket(data []byte, addr *net.UDPAddr) {
if len(data) == 0 {
return
}
pt := PacketType(data[0]) pt := PacketType(data[0])
switch pt { switch pt {
case PacketTypePing: case PacketTypePing:
err := s.sendTo([]byte{byte(PacketTypePing)}, addr) _ = s.sendTo([]byte{byte(PacketTypePing)}, addr)
if err != nil {
return
}
return return
case PacketTypeConnect: case PacketTypeConnect:
return return
@@ -109,7 +114,5 @@ func (s *Server) handlePacket(data []byte, addr *net.UDPAddr) {
return return
default: default:
return return
} }
} }