Agent skill
websockets-realtime
Quellcode ansehen: travisjneuman/.claude
Installation
npx skills add travisjneuman/.claude --skill websockets-realtime165
Installationen
EU-hosted inference API
Power your AI agent skills with open-source models.
Drop-in OpenAI-compatible API. No data leaves Europe.
MiniMax
MiniMax M3
$0.40 / $1.40
per M tokens
Z.ai
GLM 5.3 Flash
$0.20 / $0.60
per M tokens
MoonshotAI
Kimi K3
$4.00 / $18.00
per M tokens
DeepSeek
DeepSeek V4 Pro
$1.80 / $3.60
per M tokens
WebSockets & Real-Time
Comprehensive guide for building real-time applications.
Real-Time Technologies
Comparison
| Technology | Direction | Use Case |
|---|---|---|
| WebSocket | Bidirectional | Chat, gaming, collaboration |
| Server-Sent Events | Server → Client | Live feeds, notifications |
| Long Polling | Simulated bidirectional | Fallback, simple updates |
| WebRTC | Peer-to-peer | Video calls, file sharing |
When to Use What
WEBSOCKETS:
✓ Chat applications
✓ Real-time collaboration
✓ Gaming
✓ Financial trading
✓ IoT dashboards
✓ Any bidirectional communication
SERVER-SENT EVENTS (SSE):
✓ Live feeds (news, sports)
✓ Notifications
✓ Progress updates
✓ Server-initiated updates only
LONG POLLING:
✓ Fallback when WebSocket unavailable
✓ Simple, infrequent updates
✓ Behind strict firewalls
WEBRTC:
✓ Video/audio calls
✓ Screen sharing
✓ Peer-to-peer file transferWebSocket Fundamentals
How WebSockets Work
HTTP Upgrade Handshake:
┌──────┐ ┌──────┐
│Client│ GET /ws HTTP/1.1 │Server│
│ │ Upgrade: websocket │ │
│ │ ──────────────────> │ │
│ │ │ │
│ │ HTTP/1.1 101 │ │
│ │ Switching Protocols │ │
│ │ <────────────────── │ │
└──────┘ └──────┘
After handshake:
┌──────┐ ┌──────┐
│Client│ <═══════════════════>│Server│
│ │ Full-duplex TCP │ │
│ │ Binary or text │ │
└──────┘ └──────┘Client Implementation
// Basic WebSocket client
const ws = new WebSocket("wss://api.example.com/ws");
ws.onopen = () => {
console.log("Connected");
ws.send(JSON.stringify({ type: "subscribe", channel: "updates" }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
};
ws.onclose = (event) => {
console.log("Disconnected:", event.code, event.reason);
};
// Send message
ws.send(JSON.stringify({ type: "message", content: "Hello!" }));
// Close connection
ws.close(1000, "Normal closure");Reconnection Logic
class ReconnectingWebSocket {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 10;
private reconnectDelay = 1000;
constructor(private url: string) {
this.connect();
}
private connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log("Connected");
this.reconnectAttempts = 0;
};
this.ws.onclose = (event) => {
if (event.code !== 1000) {
this.reconnect();
}
};
this.ws.onerror = () => {
this.ws?.close();
};
}
private reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error("Max reconnection attempts reached");
return;
}
this.reconnectAttempts++;
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
console.log(
`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`,
);
setTimeout(() => this.connect(), delay);
}
send(data: unknown) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
}Server Implementation (Node.js)
ws Library
import { WebSocketServer, WebSocket } from "ws";
import { createServer } from "http";
const server = createServer();
const wss = new WebSocketServer({ server });
// Track connected clients
const clients = new Set<WebSocket>();
wss.on("connection", (ws, request) => {
console.log("Client connected");
clients.add(ws);
// Send welcome message
ws.send(JSON.stringify({ type: "connected", clientCount: clients.size }));
ws.on("message", (data) => {
try {
const message = JSON.parse(data.toString());
handleMessage(ws, message);
} catch (error) {
ws.send(JSON.stringify({ type: "error", message: "Invalid JSON" }));
}
});
ws.on("close", () => {
clients.delete(ws);
console.log("Client disconnected");
});
ws.on("error", (error) => {
console.error("WebSocket error:", error);
});
// Heartbeat to detect stale connections
ws.isAlive = true;
ws.on("pong", () => {
ws.isAlive = true;
});
});
// Heartbeat interval
const heartbeatInterval = setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on("close", () => {
clearInterval(heartbeatInterval);
});
function handleMessage(ws: WebSocket, message: any) {
switch (message.type) {
case "broadcast":
broadcast(message.content);
break;
case "private":
// Handle private messages
break;
default:
ws.send(
JSON.stringify({ type: "error", message: "Unknown message type" }),
);
}
}
function broadcast(content: any) {
const message = JSON.stringify({ type: "broadcast", content });
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
server.listen(3000);Socket.IO
import { Server } from "socket.io";
import { createServer } from "http";
const httpServer = createServer();
const io = new Server(httpServer, {
cors: {
origin: "https://example.com",
methods: ["GET", "POST"],
},
});
// Namespace for chat
const chat = io.of("/chat");
chat.on("connection", (socket) => {
console.log("User connected:", socket.id);
// Join room
socket.on("join", (room: string) => {
socket.join(room);
socket.to(room).emit("user_joined", { userId: socket.id });
});
// Handle message
socket.on("message", (data: { room: string; content: string }) => {
chat.to(data.room).emit("message", {
from: socket.id,
content: data.content,
timestamp: Date.now(),
});
});
// Leave room
socket.on("leave", (room: string) => {
socket.leave(room);
socket.to(room).emit("user_left", { userId: socket.id });
});
socket.on("disconnect", () => {
console.log("User disconnected:", socket.id);
});
});
// Authentication middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (validateToken(token)) {
socket.data.user = decodeToken(token);
next();
} else {
next(new Error("Authentication error"));
}
});
httpServer.listen(3000);Server-Sent Events (SSE)
Server Implementation
import express from "express";
const app = express();
app.get("/events", (req, res) => {
// Set SSE headers
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
// Send initial event
res.write("event: connected\n");
res.write('data: {"status": "connected"}\n\n');
// Send periodic updates
const interval = setInterval(() => {
const data = JSON.stringify({
timestamp: Date.now(),
value: Math.random(),
});
res.write(`data: ${data}\n\n`);
}, 1000);
// Cleanup on disconnect
req.on("close", () => {
clearInterval(interval);
res.end();
});
});
app.listen(3000);Client Implementation
const eventSource = new EventSource("/events");
eventSource.onopen = () => {
console.log("SSE connection opened");
};
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
};
eventSource.addEventListener("connected", (event) => {
console.log("Connected event:", event.data);
});
eventSource.onerror = (error) => {
console.error("SSE error:", error);
if (eventSource.readyState === EventSource.CLOSED) {
// Reconnect logic if needed
}
};
// Close connection
eventSource.close();Message Protocols
JSON Message Format
// Define message types
interface BaseMessage {
type: string;
timestamp: number;
id: string;
}
interface ChatMessage extends BaseMessage {
type: "chat";
room: string;
content: string;
sender: string;
}
interface PresenceMessage extends BaseMessage {
type: "presence";
status: "online" | "offline" | "away";
userId: string;
}
interface ErrorMessage extends BaseMessage {
type: "error";
code: string;
message: string;
}
type Message = ChatMessage | PresenceMessage | ErrorMessage;
// Type-safe message handling
function handleMessage(data: string) {
const message: Message = JSON.parse(data);
switch (message.type) {
case "chat":
displayChatMessage(message);
break;
case "presence":
updateUserPresence(message);
break;
case "error":
handleError(message);
break;
}
}Binary Protocols
// For high-performance needs, use binary formats
// MessagePack
import { encode, decode } from "@msgpack/msgpack";
const encoded = encode({ type: "position", x: 100, y: 200 });
ws.send(encoded);
ws.onmessage = (event) => {
const data = decode(event.data);
};
// Protocol Buffers
// Define schema in .proto file, generate types
// Smaller messages, faster serializationScaling WebSockets
Architecture
Load Balancer
(Sticky Sessions)
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Server 1│ │ Server 2│ │ Server 3│
│ (Node) │ │ (Node) │ │ (Node) │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────────┼────────────────┘
│
┌─────────┐
│ Redis │
│ Pub/Sub │
└─────────┘Redis Pub/Sub for Cross-Server Messages
import Redis from "ioredis";
import { WebSocketServer } from "ws";
const pub = new Redis();
const sub = new Redis();
const wss = new WebSocketServer({ port: 3000 });
// Subscribe to channel
sub.subscribe("broadcast");
// Forward Redis messages to local clients
sub.on("message", (channel, message) => {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
// Publish messages to Redis
function broadcast(message: object) {
pub.publish("broadcast", JSON.stringify(message));
}
// Receive from WebSocket, publish to Redis
wss.on("connection", (ws) => {
ws.on("message", (data) => {
broadcast(JSON.parse(data.toString()));
});
});Socket.IO with Redis Adapter
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";
const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
const io = new Server();
io.adapter(createAdapter(pubClient, subClient));
// Now messages are automatically synchronized across servers
io.emit("notification", { message: "Hello all servers!" });React Integration
Custom Hook
import { useEffect, useRef, useState, useCallback } from 'react';
interface UseWebSocketOptions {
url: string;
onMessage?: (data: any) => void;
reconnect?: boolean;
}
export function useWebSocket(options: UseWebSocketOptions) {
const { url, onMessage, reconnect = true } = options;
const wsRef = useRef<WebSocket | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [lastMessage, setLastMessage] = useState<any>(null);
const connect = useCallback(() => {
const ws = new WebSocket(url);
ws.onopen = () => setIsConnected(true);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setLastMessage(data);
onMessage?.(data);
};
ws.onclose = () => {
setIsConnected(false);
if (reconnect) {
setTimeout(connect, 3000);
}
};
wsRef.current = ws;
}, [url, onMessage, reconnect]);
useEffect(() => {
connect();
return () => {
wsRef.current?.close();
};
}, [connect]);
const send = useCallback((data: any) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(data));
}
}, []);
return { isConnected, lastMessage, send };
}
// Usage
function ChatComponent() {
const { isConnected, lastMessage, send } = useWebSocket({
url: 'wss://api.example.com/chat',
onMessage: (data) => {
console.log('New message:', data);
},
});
return (
<div>
<span>Status: {isConnected ? 'Connected' : 'Disconnected'}</span>
<button onClick={() => send({ type: 'message', content: 'Hello!' })}>
Send
</button>
</div>
);
}Socket.IO Client
import { io, Socket } from 'socket.io-client';
import { createContext, useContext, useEffect, useState } from 'react';
const SocketContext = createContext<Socket | null>(null);
export function SocketProvider({ children }: { children: React.ReactNode }) {
const [socket, setSocket] = useState<Socket | null>(null);
useEffect(() => {
const newSocket = io('https://api.example.com', {
auth: { token: getAuthToken() },
});
setSocket(newSocket);
return () => {
newSocket.close();
};
}, []);
return (
<SocketContext.Provider value={socket}>{children}</SocketContext.Provider>
);
}
export function useSocket() {
const socket = useContext(SocketContext);
if (!socket) {
throw new Error('useSocket must be used within SocketProvider');
}
return socket;
}
// Usage
function ChatRoom({ roomId }: { roomId: string }) {
const socket = useSocket();
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
socket.emit('join', roomId);
socket.on('message', (message: Message) => {
setMessages((prev) => [...prev, message]);
});
return () => {
socket.emit('leave', roomId);
socket.off('message');
};
}, [socket, roomId]);
const sendMessage = (content: string) => {
socket.emit('message', { room: roomId, content });
};
return (/* render messages */);
}Security
Authentication
// Token-based authentication
const ws = new WebSocket("wss://api.example.com/ws");
ws.onopen = () => {
// Send auth message immediately
ws.send(
JSON.stringify({
type: "auth",
token: localStorage.getItem("token"),
}),
);
};
// Server-side validation
wss.on("connection", (ws, request) => {
let authenticated = false;
const authTimeout = setTimeout(() => {
if (!authenticated) {
ws.close(4001, "Authentication timeout");
}
}, 5000);
ws.on("message", (data) => {
const message = JSON.parse(data.toString());
if (message.type === "auth") {
if (validateToken(message.token)) {
authenticated = true;
clearTimeout(authTimeout);
ws.send(JSON.stringify({ type: "auth_success" }));
} else {
ws.close(4002, "Invalid token");
}
} else if (!authenticated) {
ws.close(4003, "Not authenticated");
}
});
});Rate Limiting
const rateLimits = new Map<WebSocket, { count: number; timestamp: number }>();
function checkRateLimit(ws: WebSocket): boolean {
const now = Date.now();
const limit = rateLimits.get(ws);
if (!limit || now - limit.timestamp > 1000) {
rateLimits.set(ws, { count: 1, timestamp: now });
return true;
}
if (limit.count >= 10) {
// 10 messages per second
return false;
}
limit.count++;
return true;
}
ws.on("message", (data) => {
if (!checkRateLimit(ws)) {
ws.send(JSON.stringify({ type: "error", message: "Rate limit exceeded" }));
return;
}
// Process message
});Best Practices
DO:
- Use WSS (WebSocket Secure) in production
- Implement heartbeat/ping-pong
- Handle reconnection gracefully
- Authenticate connections
- Validate all incoming messages
- Use message IDs for acknowledgment
- Implement backpressure handling
- Monitor connection health
DON'T:
- Trust client data without validation
- Send sensitive data without encryption
- Keep connections open indefinitely
- Ignore disconnection handling
- Block the message handler
- Send unbounded data
- Forget about horizontal scaling
Troubleshooting
Common Issues
| Problem | Cause | Solution |
|---|---|---|
| Connection drops | Idle timeout | Implement heartbeat |
| Messages lost | No acknowledgment | Add message IDs + acks |
| High latency | Large messages | Use binary, compress |
| Memory leak | Unclosed connections | Proper cleanup |
| Cross-origin blocked | Missing CORS | Configure server CORS |
Debug Logging
// Development logging
if (process.env.NODE_ENV === "development") {
ws.on("message", (data) => {
console.log("← Received:", JSON.parse(data.toString()));
});
const originalSend = ws.send.bind(ws);
ws.send = (data: string) => {
console.log("→ Sending:", JSON.parse(data));
originalSend(data);
};
}Installationen
Sicherheitsprüfung
Quellcode ansehen
travisjneuman/.claude
Mehr aus dieser Quelle
Give agents clean web context
Search and extract the public web as Markdown or structured JSON through one API or hosted MCP server.
Verwandte Skills
Mehr aus dieser Quelle: travisjneuman/.claude
So verwenden Sie diesen Skill
Install websockets-realtime by running npx skills add travisjneuman/.claude --skill websockets-realtime in your project directory. Führen Sie den obigen Installationsbefehl in Ihrem Projektverzeichnis aus. Die Skill-Datei wird von GitHub heruntergeladen und in Ihrem Projekt platziert.
Keine Konfiguration erforderlich. Ihr KI-Agent (Claude Code, Cursor, Windsurf usw.) erkennt installierte Skills automatisch und nutzt sie als Kontext bei der Code-Generierung.
Der Skill verbessert das Verständnis Ihres Agenten für websockets-realtime, und hilft ihm, etablierte Muster zu befolgen, häufige Fehler zu vermeiden und produktionsreifen Code zu erzeugen.
Was Sie erhalten
Skills sind Klartext-Anweisungsdateien — kein ausführbarer Code. Sie kodieren Expertenwissen über Frameworks, Sprachen oder Tools, das Ihr KI-Agent liest, um seine Ausgabe zu verbessern. Das bedeutet null Laufzeit-Overhead, keine Abhängigkeitskonflikte und volle Transparenz: Sie können jede Anweisung vor der Installation lesen und prüfen.
Kompatibilität
Dieser Skill funktioniert mit jedem KI-Coding-Agenten, der das skills.sh-Format unterstützt, einschließlich Claude Code (Anthropic), Cursor, Windsurf, Cline, Aider und anderen Tools, die projektbezogene Kontextdateien lesen. Skills sind auf Transportebene framework-agnostisch — der Inhalt bestimmt, für welche Sprache oder welches Framework er gilt.