重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
websockets by pluginagentmarketplace/custom-plugin-nodejs
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-nodejs --skill websockets掌握实时双向通信,用于构建聊天应用、实时通知、协作工具和实时仪表板。
WebSocket 服务器只需 3 步:
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST']
}
});
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
// Handle events
socket.on('chat:message', (data) => {
// Broadcast to all clients
io.emit('chat:message', {
...data,
timestamp: Date.now()
});
});
// Join rooms
socket.on('room:join', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('room:user-joined', socket.id);
});
// Handle disconnect
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
httpServer.listen(3000);
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000', {
auth: { token: 'jwt-token-here' }
});
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('chat:message', (message) => {
console.log('Received:', message);
});
// Send message
socket.emit('chat:message', {
text: 'Hello!',
userId: 'user123'
});
const WebSocket = require('ws');
const { createServer } = require('http');
const server = createServer();
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
const ip = req.socket.remoteAddress;
console.log('Client connected from', ip);
// Handle messages
ws.on('message', (data) => {
const message = JSON.parse(data);
console.log('Received:', message);
// Echo back
ws.send(JSON.stringify({
type: 'echo',
data: message
}));
});
// Ping/pong heartbeat
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
// Heartbeat interval
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) return ws.terminate();
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on('close', () => clearInterval(interval));
server.listen(3000);
// Namespaces for feature separation
const chatNamespace = io.of('/chat');
const notificationsNamespace = io.of('/notifications');
chatNamespace.on('connection', (socket) => {
// Chat-specific logic
socket.on('message', (msg) => {
chatNamespace.emit('message', msg);
});
});
notificationsNamespace.on('connection', (socket) => {
// Notification-specific logic
socket.on('subscribe', (topic) => {
socket.join(topic);
});
});
// Rooms within namespace
socket.on('join-channel', (channelId) => {
socket.join(`channel:${channelId}`);
// Send only to room
io.to(`channel:${channelId}`).emit('user-joined', {
userId: socket.userId,
channelId
});
});
// Leave room
socket.on('leave-channel', (channelId) => {
socket.leave(`channel:${channelId}`);
});
const jwt = require('jsonwebtoken');
// Socket.io middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) {
return next(new Error('Authentication required'));
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
socket.userId = decoded.id;
socket.userRole = decoded.role;
next();
} catch (err) {
next(new Error('Invalid token'));
}
});
// Namespace-level auth
const adminNamespace = io.of('/admin');
adminNamespace.use((socket, next) => {
if (socket.userRole !== 'admin') {
return next(new Error('Admin access required'));
}
next();
});
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter(createAdapter(pubClient, subClient));
console.log('Redis adapter connected');
});
// Now events are broadcast across all server instances
io.emit('notification', { message: 'Hello from any server!' });
// Server
io.on('connection', (socket) => {
socket.on('chat:join', ({ room, username }) => {
socket.join(room);
socket.to(room).emit('chat:user-joined', { username });
});
socket.on('chat:message', ({ room, message }) => {
io.to(room).emit('chat:message', {
user: socket.userId,
message,
timestamp: Date.now()
});
});
socket.on('chat:typing', ({ room }) => {
socket.to(room).emit('chat:typing', { user: socket.userId });
});
});
// Server
function sendNotification(userId, notification) {
io.to(`user:${userId}`).emit('notification', notification);
}
// Join user's personal room
socket.on('authenticate', () => {
socket.join(`user:${socket.userId}`);
});
// From any service
notificationService.on('new', (userId, data) => {
sendNotification(userId, data);
});
// Server: broadcast metrics periodically
setInterval(async () => {
const metrics = await getSystemMetrics();
io.emit('metrics:update', metrics);
}, 1000);
// Or on-demand updates
database.onChange((change) => {
io.to(`dashboard:${change.collection}`).emit('data:update', change);
});
// Client-side reconnection
const socket = io({
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000
});
socket.on('connect_error', (err) => {
console.log('Connection error:', err.message);
});
socket.on('reconnect', (attemptNumber) => {
console.log('Reconnected after', attemptNumber, 'attempts');
});
socket.on('reconnect_failed', () => {
console.log('Failed to reconnect');
// Fallback to polling or show offline UI
});
// Server-side error handling
socket.on('error', (error) => {
console.error('Socket error:', error);
});
const { createServer } = require('http');
const { Server } = require('socket.io');
const Client = require('socket.io-client');
describe('WebSocket Server', () => {
let io, serverSocket, clientSocket;
beforeAll((done) => {
const httpServer = createServer();
io = new Server(httpServer);
httpServer.listen(() => {
const port = httpServer.address().port;
clientSocket = Client(`http://localhost:${port}`);
io.on('connection', (socket) => {
serverSocket = socket;
});
clientSocket.on('connect', done);
});
});
afterAll(() => {
io.close();
clientSocket.close();
});
it('should receive message from client', (done) => {
serverSocket.on('hello', (arg) => {
expect(arg).toBe('world');
done();
});
clientSocket.emit('hello', 'world');
});
it('should broadcast to clients', (done) => {
clientSocket.on('broadcast', (arg) => {
expect(arg).toBe('everyone');
done();
});
io.emit('broadcast', 'everyone');
});
});
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 连接断开 | 无心跳 | 启用 pingInterval/pingTimeout |
| 消息未收到 | 错误的房间 | 验证房间成员资格 |
| 扩展问题 | 无 Redis | 添加 Redis 适配器 |
| 内存泄漏 | 监听器未移除 | 在断开连接时清理 |
在以下情况使用 WebSockets:
每周安装量
49
仓库
GitHub 星标数
1
首次出现
2026 年 1 月 21 日
安全审计
安装于
opencode39
codex37
gemini-cli35
claude-code31
github-copilot30
cursor29
Master real-time bidirectional communication for building chat apps, live notifications, collaborative tools, and real-time dashboards.
WebSocket server in 3 steps:
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST']
}
});
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
// Handle events
socket.on('chat:message', (data) => {
// Broadcast to all clients
io.emit('chat:message', {
...data,
timestamp: Date.now()
});
});
// Join rooms
socket.on('room:join', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('room:user-joined', socket.id);
});
// Handle disconnect
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
httpServer.listen(3000);
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000', {
auth: { token: 'jwt-token-here' }
});
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('chat:message', (message) => {
console.log('Received:', message);
});
// Send message
socket.emit('chat:message', {
text: 'Hello!',
userId: 'user123'
});
const WebSocket = require('ws');
const { createServer } = require('http');
const server = createServer();
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
const ip = req.socket.remoteAddress;
console.log('Client connected from', ip);
// Handle messages
ws.on('message', (data) => {
const message = JSON.parse(data);
console.log('Received:', message);
// Echo back
ws.send(JSON.stringify({
type: 'echo',
data: message
}));
});
// Ping/pong heartbeat
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
// Heartbeat interval
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) return ws.terminate();
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on('close', () => clearInterval(interval));
server.listen(3000);
// Namespaces for feature separation
const chatNamespace = io.of('/chat');
const notificationsNamespace = io.of('/notifications');
chatNamespace.on('connection', (socket) => {
// Chat-specific logic
socket.on('message', (msg) => {
chatNamespace.emit('message', msg);
});
});
notificationsNamespace.on('connection', (socket) => {
// Notification-specific logic
socket.on('subscribe', (topic) => {
socket.join(topic);
});
});
// Rooms within namespace
socket.on('join-channel', (channelId) => {
socket.join(`channel:${channelId}`);
// Send only to room
io.to(`channel:${channelId}`).emit('user-joined', {
userId: socket.userId,
channelId
});
});
// Leave room
socket.on('leave-channel', (channelId) => {
socket.leave(`channel:${channelId}`);
});
const jwt = require('jsonwebtoken');
// Socket.io middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) {
return next(new Error('Authentication required'));
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
socket.userId = decoded.id;
socket.userRole = decoded.role;
next();
} catch (err) {
next(new Error('Invalid token'));
}
});
// Namespace-level auth
const adminNamespace = io.of('/admin');
adminNamespace.use((socket, next) => {
if (socket.userRole !== 'admin') {
return next(new Error('Admin access required'));
}
next();
});
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter(createAdapter(pubClient, subClient));
console.log('Redis adapter connected');
});
// Now events are broadcast across all server instances
io.emit('notification', { message: 'Hello from any server!' });
// Server
io.on('connection', (socket) => {
socket.on('chat:join', ({ room, username }) => {
socket.join(room);
socket.to(room).emit('chat:user-joined', { username });
});
socket.on('chat:message', ({ room, message }) => {
io.to(room).emit('chat:message', {
user: socket.userId,
message,
timestamp: Date.now()
});
});
socket.on('chat:typing', ({ room }) => {
socket.to(room).emit('chat:typing', { user: socket.userId });
});
});
// Server
function sendNotification(userId, notification) {
io.to(`user:${userId}`).emit('notification', notification);
}
// Join user's personal room
socket.on('authenticate', () => {
socket.join(`user:${socket.userId}`);
});
// From any service
notificationService.on('new', (userId, data) => {
sendNotification(userId, data);
});
// Server: broadcast metrics periodically
setInterval(async () => {
const metrics = await getSystemMetrics();
io.emit('metrics:update', metrics);
}, 1000);
// Or on-demand updates
database.onChange((change) => {
io.to(`dashboard:${change.collection}`).emit('data:update', change);
});
// Client-side reconnection
const socket = io({
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000
});
socket.on('connect_error', (err) => {
console.log('Connection error:', err.message);
});
socket.on('reconnect', (attemptNumber) => {
console.log('Reconnected after', attemptNumber, 'attempts');
});
socket.on('reconnect_failed', () => {
console.log('Failed to reconnect');
// Fallback to polling or show offline UI
});
// Server-side error handling
socket.on('error', (error) => {
console.error('Socket error:', error);
});
const { createServer } = require('http');
const { Server } = require('socket.io');
const Client = require('socket.io-client');
describe('WebSocket Server', () => {
let io, serverSocket, clientSocket;
beforeAll((done) => {
const httpServer = createServer();
io = new Server(httpServer);
httpServer.listen(() => {
const port = httpServer.address().port;
clientSocket = Client(`http://localhost:${port}`);
io.on('connection', (socket) => {
serverSocket = socket;
});
clientSocket.on('connect', done);
});
});
afterAll(() => {
io.close();
clientSocket.close();
});
it('should receive message from client', (done) => {
serverSocket.on('hello', (arg) => {
expect(arg).toBe('world');
done();
});
clientSocket.emit('hello', 'world');
});
it('should broadcast to clients', (done) => {
clientSocket.on('broadcast', (arg) => {
expect(arg).toBe('everyone');
done();
});
io.emit('broadcast', 'everyone');
});
});
| Problem | Cause | Solution |
|---|---|---|
| Connection drops | No heartbeat | Enable pingInterval/pingTimeout |
| Messages not received | Wrong room | Verify room membership |
| Scaling issues | No Redis | Add Redis adapter |
| Memory leak | Listeners not removed | Clean up on disconnect |
Use WebSockets when:
Weekly Installs
49
Repository
GitHub Stars
1
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode39
codex37
gemini-cli35
claude-code31
github-copilot30
cursor29
MCP服务器模式指南:构建AI助手工具与资源的最佳实践
1,300 周安装