Skip to content

rsecss/websocket-bench

Repository files navigation

socket-bench

CI Node.js License: MIT

Minimal WebSocket relay server for real-time message forwarding between browsers and devices.

中文文档

Features

  • Zero-config relay — no auth, no database, just connect and forward
  • Broadcast & unicast — send to all clients or target a specific one by clientId
  • Built-in debug console — multi-tab UI with dark terminal, connection presets, online clients panel, no build tools needed
  • 30s heartbeat — automatic ping/pong keep-alive, compatible with Cloudflare proxy
  • Self-contained tests — integration tests over real WebSocket connections using native Node.js assert

Quick Start

pnpm install
pnpm dev
# Open http://localhost:3000

Architecture

pnpm monorepo with two packages:

Browser ──► WS ──► Fastify(:3000) ◄── WS ◄── Device
                        │
                        ├─ Static file hosting + SPA fallback
                        └─ WebSocket relay plugin
packages/
├── shared/          # TypeScript types (WSMessage, ClientMessage, ServerMessage)
└── server/
    ├── src/
    │   ├── index.ts                    # Entry: static files, /api/ping, graceful shutdown
    │   └── plugins/websocket-relay.ts  # Core relay: client map, ping/pong, broadcast/unicast
    ├── public/
    │   ├── index.html                  # Debug console — HTML structure
    │   ├── style.css                   # Styles (Indigo light theme, dark terminal)
    │   └── app.js                      # Logic: multi-tab, presets, online clients
    └── test/websocket-relay.selftest.js

Protocol

Connect: ws://host/ws?clientId=xxx&role=web|device

Client → Server

Type Payload
ping {}
relay.send { target: "broadcast" | "<clientId>", event, data }
connections.list {}

Server → Client

Type Payload
pong {}
relay.recv { from: "<clientId>", event, data }
connections.info { clients: [{ clientId, role, connectedAt }] }
connection.notify { action: "join" | "leave", clientId, role, total, connectedAt? }
  • Duplicate clientId replaces old connection (close code 4000)
  • Missing clientId rejected (close code 4002)

Usage

import WebSocket from 'ws';

const ws = new WebSocket('ws://localhost:3000/ws?clientId=device-001&role=device');

ws.on('open', () => {
  // Send a message to all connected clients
  ws.send(
    JSON.stringify({
      type: 'relay.send',
      payload: { target: 'broadcast', event: 'sensor', data: { temp: 22.5 } },
      timestamp: Date.now(),
    }),
  );
});

ws.on('message', (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.type === 'relay.recv') {
    console.log(`From ${msg.payload.from}: ${msg.payload.event}`, msg.payload.data);
  }
});

Self-Hosted Deploy

The server can be exposed to the public internet via frp + reverse proxy. See docs/deploy-frp.md for a complete guide covering frps/frpc setup, OpenResty reverse proxy, and Cloudflare DNS configuration.

Environment Variables

Variable Default Description
API_HOST 0.0.0.0 Server listen host
API_PORT 3000 Server listen port
LOG_LEVEL info Log level

All variables have built-in defaults — no .env file required.

Contributing

See CONTRIBUTING.md for development setup, code style, and pull request guidelines.

License

MIT © 2026 Maple

About

Minimal WebSocket relay server for real-time message forwarding between browsers and devices.

Resources

License

Contributing

Stars

Watchers

Forks

Contributors