//go:build windows package server import ( "log" "time" "github.com/gorilla/websocket" ) const ( writeWait = 10 * time.Second pongWait = 60 * time.Second pingPeriod = pongWait * 9 / 10 maxMessageSize = 512 ) // hub maintains the set of active WebSocket clients and broadcasts messages. type hub struct { clients map[*wsClient]struct{} broadcast chan []byte register chan *wsClient unregister chan *wsClient onCmd func([]byte) // called for each message from any client } func newHub(onCmd func([]byte)) *hub { return &hub{ clients: make(map[*wsClient]struct{}), broadcast: make(chan []byte, 64), register: make(chan *wsClient, 8), unregister: make(chan *wsClient, 8), onCmd: onCmd, } } func (h *hub) run() { for { select { case c := <-h.register: h.clients[c] = struct{}{} case c := <-h.unregister: if _, ok := h.clients[c]; ok { delete(h.clients, c) close(c.send) } case msg := <-h.broadcast: for c := range h.clients { select { case c.send <- msg: default: // Slow client — drop and disconnect. close(c.send) delete(h.clients, c) } } } } } // ── wsClient ────────────────────────────────────────────────────────────────── type wsClient struct { hub *hub conn *websocket.Conn send chan []byte } func (c *wsClient) readPump() { defer func() { c.hub.unregister <- c c.conn.Close() }() c.conn.SetReadLimit(maxMessageSize) c.conn.SetReadDeadline(time.Now().Add(pongWait)) c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)) return nil }) for { _, msg, err := c.conn.ReadMessage() if err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { log.Printf("ws read: %v", err) } break } if c.hub.onCmd != nil { c.hub.onCmd(msg) } } } func (c *wsClient) writePump() { ticker := time.NewTicker(pingPeriod) defer func() { ticker.Stop() c.conn.Close() }() for { select { case msg, ok := <-c.send: c.conn.SetWriteDeadline(time.Now().Add(writeWait)) if !ok { c.conn.WriteMessage(websocket.CloseMessage, []byte{}) return } if err := c.conn.WriteMessage(websocket.TextMessage, msg); err != nil { return } case <-ticker.C: c.conn.SetWriteDeadline(time.Now().Add(writeWait)) if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { return } } } }