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