← Sandbox SDK / sandbox / guides
WebSocket-соединения
В этом руководстве показано, как работать с серверами WebSocket, запущенными в ваших песочницах.
Выбор подхода
Публикация через preview URL - Получите публичный URL, по которому смогут подключаться внешние клиенты. Подходит для публичных чатов, многопользовательских игр или дашбордов в реальном времени.
Подключайтесь с помощью wsConnect() - Ваш Worker устанавливает соединение WebSocket. Подходит для нестандартной логики маршрутизации, шлюзов аутентификации или случаев, когда вашему Worker нужны данные от служб песочницы в реальном времени.
Подключитесь к echo-серверу WebSocket
Создайте эхо-сервер:
Bun.serve({
port: 8080,
hostname: "0.0.0.0",
fetch(req, server) {
if (server.upgrade(req)) {
return;
}
return new Response("WebSocket echo server");
},
websocket: {
message(ws, message) {
ws.send(`Echo: ${message}`);
},
open(ws) {
console.log("Client connected");
},
close(ws) {
console.log("Client disconnected");
},
},
});
console.log("WebSocket server listening on port 8080");Дополните Dockerfile:
FROM docker.io/cloudflare/sandbox:0.3.3
# Copy echo server into the container
COPY echo-server.ts /workspace/echo-server.ts
# Create custom startup script
COPY startup.sh /container-server/startup.sh
RUN chmod +x /container-server/startup.shСоздайте скрипт запуска:
#!/bin/bash
# Start your WebSocket server in the background
bun /workspace/echo-server.ts &
# Start SDK's control plane (needed for the SDK to work)
exec bun dist/index.jsПодключайтесь из своего Worker:
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request, env) {
if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") {
const sandbox = getSandbox(env.Sandbox, "echo-service");
return await sandbox.wsConnect(request, 8080);
}
return new Response("WebSocket endpoint");
},
};import { getSandbox } from '@cloudflare/sandbox';
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.headers.get('Upgrade')?.toLowerCase() === 'websocket') {
const sandbox = getSandbox(env.Sandbox, 'echo-service');
return await sandbox.wsConnect(request, 8080);
}
return new Response('WebSocket endpoint');
}
};Клиент подключается:
const ws = new WebSocket('wss://your-worker.com');
ws.onmessage = (event) => console.log(event.data);
ws.send('Hello!'); // Receives: "Echo: Hello!"Публикация WebSocket-сервиса через preview URL
Получите публичный URL для своего WebSocket-сервера:
import { getSandbox, proxyToSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request, env) {
// Auto-route all requests via proxyToSandbox first
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
// Extract hostname from request
const { hostname } = new URL(request.url);
const sandbox = getSandbox(env.Sandbox, "echo-service");
// Expose the port to get preview URL
const { url } = await sandbox.exposePort(8080, { hostname });
// Return URL to clients
if (request.url.includes("/ws-url")) {
return Response.json({ url: url.replace("https", "wss") });
}
return new Response("Not found", { status: 404 });
},
};import { getSandbox, proxyToSandbox } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Auto-route all requests via proxyToSandbox first
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
// Extract hostname from request
const { hostname } = new URL(request.url);
const sandbox = getSandbox(env.Sandbox, 'echo-service');
// Expose the port to get preview URL
const { url } = await sandbox.exposePort(8080, { hostname });
// Return URL to clients
if (request.url.includes('/ws-url')) {
return Response.json({ url: url.replace('https', 'wss') });
}
return new Response('Not found', { status: 404 });
}
};Клиент подключается к URL-адресу предварительного просмотра:
// Get the preview URL
const response = await fetch('https://your-worker.com/ws-url');
const { url } = await response.json();
// Connect
const ws = new WebSocket(url);
ws.onmessage = (event) => console.log(event.data);
ws.send('Hello!'); // Receives: "Echo: Hello!"Подключайтесь из Worker, чтобы получать данные в реальном времени
Ваш Worker может подключаться к сервису WebSocket для получения данных в реальном времени, даже если входящий запрос не является WebSocket-запросом:
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
let initialized = false;
export default {
async fetch(request, env) {
// Get or create a sandbox instance
const sandbox = getSandbox(env.Sandbox, "data-processor");
// Check for WebSocket upgrade
const upgrade = request.headers.get("Upgrade")?.toLowerCase();
if (upgrade === "websocket") {
// Initialize server on first connection
if (!initialized) {
await sandbox.writeFile(
"/workspace/server.js",
`Bun.serve({
port: 8080,
fetch(req, server) {
server.upgrade(req);
},
websocket: {
message(ws, msg) {
ws.send(\`Echo: \${msg}\`);
}
}
});`,
);
await sandbox.startProcess("bun /workspace/server.js");
initialized = true;
}
// Connect to WebSocket server
return await sandbox.wsConnect(request, 8080);
}
return new Response("Processed real-time data");
},
};import { getSandbox } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
let initialized = false;
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Get or create a sandbox instance
const sandbox = getSandbox(env.Sandbox, 'data-processor');
// Check for WebSocket upgrade
const upgrade = request.headers.get('Upgrade')?.toLowerCase();
if (upgrade === 'websocket') {
// Initialize server on first connection
if (!initialized) {
await sandbox.writeFile(
'/workspace/server.js',
`Bun.serve({
port: 8080,
fetch(req, server) {
server.upgrade(req);
},
websocket: {
message(ws, msg) {
ws.send(\`Echo: \${msg}\`);
}
}
});`
);
await sandbox.startProcess(
'bun /workspace/server.js'
);
initialized = true;
}
// Connect to WebSocket server
return await sandbox.wsConnect(request, 8080);
}
return new Response('Processed real-time data');
}
};Такой подход полезен, когда нужно получать потоковые данные от сервисов песочницы, но возвращать клиентам HTTP-ответы.
Устранение неполадок
Сбой обновления
Проверьте, что в запросе есть заголовки WebSocket:
console.log(request.headers.get("Upgrade")); // 'websocket'
console.log(request.headers.get("Connection")); // 'Upgrade'console.log(request.headers.get('Upgrade')); // 'websocket'
console.log(request.headers.get('Connection')); // 'Upgrade'Локальная разработка
Открывайте порты в Dockerfile для wrangler dev:
FROM docker.io/cloudflare/sandbox:0.3.3
COPY echo-server.ts /workspace/echo-server.ts
COPY startup.sh /container-server/startup.sh
RUN chmod +x /container-server/startup.sh
# Required for local development
EXPOSE 8080Дополнительные материалы
- Справочник по API портов - Полная документация по API
- Концепция Preview URL - Как работают preview URL
- Tunnels API - Не требует настройки
*.trycloudflare.comURL для WebSocket-сервисов при разработке - Руководство по фоновым процессам - Управление длительными сервисами