83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
package infra
|
|
|
|
import "sync/atomic"
|
|
|
|
// WSHub gestiona el ciclo de vida de conexiones WebSocket.
|
|
// Mantiene un mapa de clientes activos y canales para registro,
|
|
// desregistro y broadcast. Se ejecuta como goroutine via su Run().
|
|
type WSHub struct {
|
|
Clients map[*WSClient]bool
|
|
Broadcast chan []byte
|
|
Register chan *WSClient
|
|
Unregister chan *WSClient
|
|
done chan struct{}
|
|
count atomic.Int64
|
|
}
|
|
|
|
// ClientCount retorna el numero de clientes registrados de forma thread-safe.
|
|
// Util para metricas, dashboards y tests.
|
|
func (h *WSHub) ClientCount() int {
|
|
return int(h.count.Load())
|
|
}
|
|
|
|
// NewWSHub crea un WSHub vacio con canales inicializados.
|
|
// Ejecutar Run() en una goroutine para activar el loop de eventos.
|
|
func NewWSHub() *WSHub {
|
|
return &WSHub{
|
|
Clients: make(map[*WSClient]bool),
|
|
Broadcast: make(chan []byte, 256),
|
|
Register: make(chan *WSClient),
|
|
Unregister: make(chan *WSClient),
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Run ejecuta el loop principal del hub. Atiende registros, desregistros
|
|
// y broadcasts en un select. Bloqueante: lanzar como goroutine.
|
|
// Para parar, llamar a Stop().
|
|
func (h *WSHub) Run() {
|
|
for {
|
|
select {
|
|
case <-h.done:
|
|
// Cerrar todos los Send y vaciar el mapa
|
|
for client := range h.Clients {
|
|
delete(h.Clients, client)
|
|
close(client.Send)
|
|
}
|
|
h.count.Store(0)
|
|
return
|
|
case client := <-h.Register:
|
|
h.Clients[client] = true
|
|
h.count.Store(int64(len(h.Clients)))
|
|
case client := <-h.Unregister:
|
|
if _, ok := h.Clients[client]; ok {
|
|
delete(h.Clients, client)
|
|
close(client.Send)
|
|
h.count.Store(int64(len(h.Clients)))
|
|
}
|
|
case msg := <-h.Broadcast:
|
|
for client := range h.Clients {
|
|
select {
|
|
case client.Send <- msg:
|
|
default:
|
|
// Cliente lento: desconectar
|
|
delete(h.Clients, client)
|
|
close(client.Send)
|
|
}
|
|
}
|
|
h.count.Store(int64(len(h.Clients)))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop cierra el loop de Run() y limpia todos los clientes.
|
|
// Idempotente — llamar mas de una vez no panica.
|
|
func (h *WSHub) Stop() {
|
|
select {
|
|
case <-h.done:
|
|
// Ya cerrado
|
|
default:
|
|
close(h.done)
|
|
}
|
|
}
|