websocket second version

main
nilo 2025-05-22 10:27:05 -03:00
parent 84de38de26
commit 2844928769
2 changed files with 77 additions and 30 deletions

View File

@ -2,8 +2,10 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"log" "log"
"sync" "sync"
"time"
"github.com/gofiber/websocket/v2" "github.com/gofiber/websocket/v2"
) )
@ -12,6 +14,7 @@ import (
type WebSocketManager struct { type WebSocketManager struct {
connections map[*websocket.Conn]bool connections map[*websocket.Conn]bool
mutex sync.RWMutex mutex sync.RWMutex
broadcast chan WebSocketMessage
} }
// WebSocketMessage represents the structure of messages // WebSocketMessage represents the structure of messages
@ -24,6 +27,37 @@ type WebSocketMessage struct {
// Global instance of WebSocketManager // Global instance of WebSocketManager
var WSManager = &WebSocketManager{ var WSManager = &WebSocketManager{
connections: make(map[*websocket.Conn]bool), connections: make(map[*websocket.Conn]bool),
broadcast: make(chan WebSocketMessage, 100), // Buffer size of 100
}
func init() {
// Start the broadcast handler
go WSManager.handleBroadcasts()
}
func (m *WebSocketManager) handleBroadcasts() {
for message := range m.broadcast {
jsonMessage, err := json.Marshal(message)
if err != nil {
log.Printf("Error marshaling message: %v", err)
continue
}
m.mutex.RLock()
for conn := range m.connections {
// Send message asynchronously
go func(c *websocket.Conn) {
writeTimeout := time.Now().Add(time.Second * 5)
c.SetWriteDeadline(writeTimeout)
if err := c.WriteMessage(websocket.TextMessage, jsonMessage); err != nil {
log.Printf("Error sending message: %v", err)
m.removeConnection(c)
}
}(conn)
}
m.mutex.RUnlock()
}
} }
// BroadcastMessage sends a message to all connected clients // BroadcastMessage sends a message to all connected clients
@ -33,24 +67,13 @@ func (m *WebSocketManager) BroadcastMessage(command, channel, text string) {
Channel: channel, Channel: channel,
Text: text, Text: text,
} }
jsonMessage, err := json.Marshal(message)
if err != nil {
log.Printf("Error marshaling message: %v", err)
return
}
m.mutex.RLock() // Non-blocking send to broadcast channel
defer m.mutex.RUnlock() select {
case m.broadcast <- message:
for conn := range m.connections { // Message queued successfully
if err := conn.WriteMessage(websocket.TextMessage, jsonMessage); err != nil { default:
log.Printf("Error broadcasting message: %v", err) log.Println("Broadcast channel full, message dropped")
// Remove failed connection
m.mutex.RUnlock()
m.removeConnection(conn)
m.mutex.RLock()
}
} }
} }
@ -61,7 +84,7 @@ func (m *WebSocketManager) SendMessageToClient(conn *websocket.Conn, command, ch
Channel: channel, Channel: channel,
Text: text, Text: text,
} }
jsonMessage, err := json.Marshal(message) jsonMessage, err := json.Marshal(message)
if err != nil { if err != nil {
return err return err
@ -71,6 +94,8 @@ func (m *WebSocketManager) SendMessageToClient(conn *websocket.Conn, command, ch
defer m.mutex.RUnlock() defer m.mutex.RUnlock()
if _, exists := m.connections[conn]; exists { if _, exists := m.connections[conn]; exists {
writeTimeout := time.Now().Add(time.Second * 5)
conn.SetWriteDeadline(writeTimeout)
return conn.WriteMessage(websocket.TextMessage, jsonMessage) return conn.WriteMessage(websocket.TextMessage, jsonMessage)
} }
return nil return nil
@ -91,6 +116,9 @@ func (m *WebSocketManager) removeConnection(conn *websocket.Conn) {
// WebsocketHandler handles WebSocket connections // WebsocketHandler handles WebSocket connections
func WebsocketHandler(c *websocket.Conn) { func WebsocketHandler(c *websocket.Conn) {
// Set read deadline
c.SetReadDeadline(time.Now().Add(time.Second * 60)) // 1 minute timeout
// Add the connection to our manager // Add the connection to our manager
WSManager.addConnection(c) WSManager.addConnection(c)
defer WSManager.removeConnection(c) defer WSManager.removeConnection(c)
@ -103,17 +131,26 @@ func WebsocketHandler(c *websocket.Conn) {
break break
} }
// Try to parse the incoming message as JSON // Process message asynchronously
var wsMessage WebSocketMessage go func() {
if err := json.Unmarshal(message, &wsMessage); err != nil { var wsMessage WebSocketMessage
log.Printf("Error parsing message: %v", err) if err := json.Unmarshal(message, &wsMessage); err != nil {
continue log.Printf("Error parsing message: %v", err)
} return
}
// Echo the message back to the client switch wsMessage.Command {
if err := WSManager.SendMessageToClient(c, wsMessage.Command, wsMessage.Channel, wsMessage.Text); err != nil { case "chat":
log.Println("Error writing message:", err) WSManager.BroadcastMessage("chat", wsMessage.Channel, wsMessage.Text)
break case "join":
} WSManager.BroadcastMessage("system", wsMessage.Channel,
fmt.Sprintf("User joined channel %s", wsMessage.Channel))
case "leave":
WSManager.BroadcastMessage("system", wsMessage.Channel,
fmt.Sprintf("User left channel %s", wsMessage.Channel))
default:
WSManager.SendMessageToClient(c, "error", "", "Unknown command")
}
}()
} }
} }

12
main.go
View File

@ -18,6 +18,7 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/websocket/v2"
) )
func main() { func main() {
@ -81,10 +82,19 @@ func main() {
})) }))
// Connects to database // Connects to database
if err := database.ConnectDB(); err != nil { if err = database.ConnectDB(); err != nil {
panic("Could not connect to database") panic("Could not connect to database")
} }
// WebSocket configuration
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
// Setup routes // Setup routes
routes.Setup(app) routes.Setup(app)