Skip to main content

websocket-patterns

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

معلومات المصدر

المستودع
skeletorflet/opencode-kit
آخر نشاط في المصدر
٣ أبريل ٢٠٢٦ في ١٥:٣٢
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٠
التفرعات
١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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 |
عرض على GitHub