Skip to content
cccskills
FreePublished on GitHub

websocket-patterns

Connection management, room patterns, reconnection strategies, message buffering, and binary protocol design.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md6.7 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

WebSocket Patterns

Production WebSocket patterns for real-time applications.

Connection Management

import { WebSocketServer, WebSocket } from 'ws'

interface Client {
  id: string
  ws: WebSocket
  rooms: Set<string>
  lastPing: number
  metadata: Record<string, unknown>
}

class ConnectionManager {
  private clients = new Map<string, Client>()
  private heartbeatInterval: NodeJS.Timeout

  constructor(private wss: WebSocketServer) {
    this.heartbeatInterval = setInterval(() => this.checkHeartbeats(), 30_000)

    wss.on('connection', (ws, req) => {
      const clientId = crypto.randomUUID()
      const client: Client = {
        id: clientId,
        ws,
        rooms: new Set(),
        lastPing: Date.now(),
        metadata: { ip: req.socket.remoteAddress }
      }
      this.clients.set(clientId, client)

      ws.on('pong', () => { client.lastPing = Date.now() })
      ws.on('close', () => this.removeClient(clientId))
      ws.on('error', () => this.removeClient(clientId))

      this.send(client, { type: 'connected', clientId })
    })
  }

  private checkHeartbeats(): void {
    const staleThreshold = Date.now() - 45_000
    for (const [id, client] of this.clients) {
      if (client.lastPing < staleThreshold) {
        client.ws.terminate()
        this.removeClient(id)
      } else {
        client.ws.ping()
      }
    }
  }

  private removeClient(id: string): void {
    const client = this.clients.get(id)
    if (!client) return
    for (const room of client.rooms) {
      this.leaveRoom(id, room)
    }
    this.clients.delete(id)
  }

  send(client: Client, data: unknown): void {
    if (client.ws.readyState === WebSocket.OPEN) {
      client.ws.send(JSON.stringify(data))
    }
  }

  destroy(): void {
    clearInterval(this.heartbeatInterval)
  }
}

Room Pattern

class RoomManager {
  private rooms = new Map<string, Set<string>>()

  joinRoom(clientId: string, room: string): void {
    if (!this.rooms.has(room)) {
      this.rooms.set(room, new Set())
    }
    this.rooms.get(room)!.add(clientId)
  }

  leaveRoom(clientId: string, room: string): void {
    const members = this.rooms.get(room)
    if (!members) return
    members.delete(clientId)
    if (members.size === 0) this.rooms.delete(room)
  }

  broadcast(room: string, data: unknown, excludeId?: string): void {
    const members = this.rooms.get(room)
    if (!members) return
    const payload = JSON.stringify(data)
    for (const id of members) {
      if (id === excludeId) continue
      const client = this.clients.get(id)
      if (client?.ws.readyState === WebSocket.OPEN) {
        client.ws.send(payload)
      }
    }
  }

  getRoomSize(room: string): number {
    return this.rooms.get(room)?.size ?? 0
  }
}

Client-Side Reconnection

class ReconnectingWebSocket {
  private ws: WebSocket | null = null
  private reconnectAttempts = 0
  private maxReconnectDelay = 30_000
  private messageBuffer: unknown[] = []
  private handlers = new Map<string, Function[]>()

  constructor(private url: string) {
    this.connect()
  }

  private connect(): void {
    this.ws = new WebSocket(this.url)

    this.ws.onopen = () => {
      this.reconnectAttempts = 0
      this.flushBuffer()
      this.emit('connected')
    }

    this.ws.onclose = (event) => {
      if (event.code === 1000) return  // Normal closure, no reconnect
      this.scheduleReconnect()
    }

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data as string)
      this.emit(data.type, data)
    }

    this.ws.onerror = () => {
      this.ws?.close()
    }
  }

  private scheduleReconnect(): void {
    // Exponential backoff with jitter
    const baseDelay = Math.min(1000 * 2 ** this.reconnectAttempts, this.maxReconnectDelay)
    const jitter = baseDelay * 0.3 * Math.random()
    const delay = baseDelay + jitter

    this.reconnectAttempts++
    setTimeout(() => this.connect(), delay)
  }

  send(data: unknown): void {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data))
    } else {
      // Buffer messages while disconnected (max 100)
      if (this.messageBuffer.length < 100) {
        this.messageBuffer.push(data)
      }
    }
  }

  private flushBuffer(): void {
    const buffered = [...this.messageBuffer]
    this.messageBuffer = []
    for (const msg of buffered) {
      this.send(msg)
    }
  }

  on(event: string, handler: Function): void {
    if (!this.handlers.has(event)) this.handlers.set(event, [])
    this.handlers.get(event)!.push(handler)
  }

  private emit(event: string, ...args: unknown[]): void {
    for (const handler of this.handlers.get(event) ?? []) {
      handler(...args)
    }
  }
}

Message Protocol Design

// Typed message envelope
interface WsMessage<T = unknown> {
  type: string
  id: string          // For request-response correlation
  timestamp: number
  payload: T
}

// Request-response over WebSocket
class WsRpc {
  private pending = new Map<string, { resolve: Function; timer: NodeJS.Timeout }>()

  async request<T>(type: string, payload: unknown, timeoutMs = 5000): Promise<T> {
    const id = crypto.randomUUID()
    const msg: WsMessage = { type, id, timestamp: Date.now(), payload }

    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        this.pending.delete(id)
        reject(new Error(`RPC timeout: ${type}`))
      }, timeoutMs)

      this.pending.set(id, { resolve, timer })
      this.ws.send(JSON.stringify(msg))
    })
  }

  handleResponse(msg: WsMessage): void {
    const entry = this.pending.get(msg.id)
    if (!entry) return
    clearTimeout(entry.timer)
    this.pending.delete(msg.id)
    entry.resolve(msg.payload)
  }
}

Checklist

  • Heartbeat/ping-pong every 30s, terminate stale connections at 45s
  • Exponential backoff with jitter for client reconnection (1s-30s)
  • Message buffer during disconnection (cap at 100 messages)
  • Auth token validation on initial handshake (not in messages)
  • Room-based broadcasting for scoped message delivery
  • Rate limit per client (max messages/second)
  • Message size limit (max 1MB per frame)
  • Graceful shutdown: close code 1001, drain messages before exit

Anti-Patterns

  • No heartbeat: zombie connections consuming resources
  • Reconnecting without backoff: hammering server on outage
  • Unbounded message buffer: memory leak during long disconnections
  • Auth in every message: use connection-level auth, not per-message
  • Broadcasting to all clients when only a room subset needs the update
  • Synchronous JSON.parse in hot path: use worker threads for heavy payloads

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

WCAG 2.2 AA compliance, ARIA patterns, keyboard navigation, screen reader optimization

The English summary is being prepared. Showing the original description.

vibeeval/vibecosystem538Updated Aug 9, 2026

axe-core integration, WCAG 2.2 AA checklist, keyboard navigation testing, screen reader testing, and ARIA pattern validation.

The English summary is being prepared. Showing the original description.

vibeeval/vibecosystem538Updated Aug 9, 2026

Steam-style achievement system with XP, levels, streaks, and skill trees. Gamifies the development workflow. 25 achievements across 5 categories.

The English summary is being prepared. Showing the original description.

vibeeval/vibecosystem538Updated Aug 9, 2026

Framework for measuring and tracking agent response quality over time. Detects regressions before they reach production. Use when evaluating agent changes, auditing quality, or establishing performance baselines.

The English summary is being prepared. Showing the original description.

vibeeval/vibecosystem538Updated Aug 9, 2026

Agent Context Isolation

The English summary is being prepared. Showing the original description.

vibeeval/vibecosystem538Updated Aug 9, 2026

Agent ve skill dosyalarinin yapisal dogrulamasi. Frontmatter kontrol, naming convention, zorunlu bolum kontrolu, tutarlilik denetimi. Yeni agent/skill eklendiginde veya mevcut dosyalar duzenlediginde otomatik calistirilir.

The English summary is being prepared. Showing the original description.

vibeeval/vibecosystem538Updated Aug 9, 2026

See all skills by vibeeval

Report a problem with this skill