Skip to main content

websocket-patterns

WebSocket and real-time patterns. Socket.io, native WebSocket, Server-Sent Events, connection management, rooms.

Informations de source

Dépôt
skeletorflet/opencode-kit
Dernière activité de la source
3 avril 2026 à 15:32
Langue détectée de SKILL.md
anglais
Étoiles
9
Forks
1

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
websocket-patterns
description
WebSocket and real-time patterns. Socket.io, native WebSocket, Server-Sent Events, connection management, rooms.
# WebSocket Patterns > Use the right real-time primitive. Not everything needs a WebSocket. --- ## 1. Real-Time Technology Choice | Technology | Direction | When | |-----------|-----------|------| | **WebSocket** | Bidirectional | Chat, gaming, live collab | | **Server-Sent Events (SSE)** | Server → Client | Live feeds, notifications | | **HTTP Polling** | Client-initiated | Simple, compatibility | | **Long Polling** | Client-initiated | When WebSocket blocked | ``` SSE is simpler than WebSocket for one-way data. Use WebSocket only when client needs to send data frequently. ``` --- ## 2. Socket.io Server ```ts import { Server } from "socket.io"; import { createServer } from "http"; const httpServer = createServer(app); const io = new Server(httpServer, { cors: { origin: process.env.CLIENT_URL, credentials: true }, pingTimeout: 60_000, pingInterval: 25_000, }); // Authentication middleware io.use(async (socket, next) => { try { const token = socket.handshake.auth.token; const user = await verifyToken(token); socket.data.user = user; next(); } catch { next(new Error("Authentication error")); } }); io.on("connection", (socket) => { const { user } = socket.data; console.log(`${user.id} connected`); // Join room socket.on("join:room", (roomId: string) => { socket.join(roomId); socket.to(roomId).emit("user:joined", { userId: user.id }); }); // Message socket.on("message:send", async ({ roomId, content }) => { const message = await db.message.create({ data: { content, authorId: user.id, roomId }, include: { author: true }, }); io.to(roomId).emit("message:new", message); }); socket.on("disconnect", (reason) => { console.log(`${user.id} disconnected: ${reason}`); }); }); ``` --- ## 3. Socket.io Client (React) ```tsx import { createContext, useContext, useEffect, useState } from "react"; import { io, Socket } from "socket.io-client"; const SocketContext = createContext<Socket | null>(null); export function SocketProvider({ children }: { children: React.ReactNode }) { const [socket, setSocket] = useState<Socket | null>(null); const { token } = useAuth(); useEffect(() => { if (!token) return; const s = io(process.env.NEXT_PUBLIC_WS_URL!, { auth: { token }, transports: ["websocket"], reconnectionAttempts: 5, reconnectionDelay: 1000, }); s.on("connect", () => console.log("Connected")); s.on("connect_error", (err) => console.error("Connection error:", err)); setSocket(s); return () => { s.disconnect(); }; }, [token]); return <SocketContext.Provider value={socket}>{children}</SocketContext.Provider>; } // Usage in component function ChatRoom({ roomId }: { roomId: string }) { const socket = useContext(SocketContext); const [messages, setMessages] = useState<Message[]>([]); useEffect(() => { if (!socket) return; socket.emit("join:room", roomId); socket.on("message:new", (msg) => setMessages(prev => [...prev, msg])); return () => { socket.off("message:new"); socket.emit("leave:room", roomId); }; }, [socket, roomId]); } ``` --- ## 4. Server-Sent Events (SSE) ```ts // Express SSE endpoint app.get("/events", auth, (req, res) => { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.flushHeaders(); // Send event const send = (event: string, data: unknown) => { res.write(`event: ${event}\\\ data: ${JSON.stringify(data)}\\\ \\\ `); }; // Subscribe to updates const unsubscribe = pubSub.subscribe(`user:${req.user.id}`, (event) => { send(event.type, event.data); }); // Heartbeat to prevent timeout const heartbeat = setInterval(() => res.write(": ping\\\ \\\ "), 30_000); req.on("close", () => { clearInterval(heartbeat); unsubscribe(); }); }); ``` ```tsx // Client SSE useEffect(() => { const es = new EventSource("/events", { withCredentials: true }); es.addEventListener("notification", (e) => { addNotification(JSON.parse(e.data)); }); es.onerror = () => setTimeout(() => es.close(), 1000); // reconnect return () => es.close(); }, []); ``` --- ## 5. Scaling WebSockets ``` Single server: Socket.io in-process Multiple servers: Need sticky sessions + Redis adapter Redis adapter (Socket.io): import { createAdapter } from "@socket.io/redis-adapter"; io.adapter(createAdapter(pubClient, subClient)); → Events now propagate across all server instances → io.to(room).emit() works across servers ``` --- ## 6. Connection Management ``` Reconnection: ├── Exponential backoff (1s, 2s, 4s, 8s, max 30s) ├── Stop retry after N attempts ├── Notify user if offline for extended period └── Re-sync state on reconnect (not just resume) Presence: ├── Track connected sockets per user in Redis ├── Publish presence events on connect/disconnect └── Debounce disconnect (user may have briefly lost connection) ``` --- ## 7. Anti-Patterns | ❌ Don\'t | ✅ Do | |----------|-------| | WebSocket for read-only data | SSE (simpler) | | No auth on WebSocket | Auth in middleware | | Emit to all clients | Use rooms/namespaces | | No reconnect logic | Always implement reconnect | | Store socket connections in DB | Use in-memory + Redis |
Voir sur GitHub