INTEGRITY Dokumentace

Použití WebSockets API

WebSockets vám umožňují komunikovat v reálném čase s vašimi serverless funkcemi Cloudflare Workers. V tomto návodu se naučíte základy WebSockets na Cloudflare Workers, a to jak z pohledu psaní serverů WebSocket ve vašich funkcích Workers, tak z pohledu připojování k těmto serverům WebSocket a práce s nimi jako klient.

WebSockets jsou otevřená spojení udržovaná mezi klientem a origin serverem. V rámci spojení WebSocket si klient a origin mohou předávat data tam a zpět, aniž by museli znovu navazovat relace. Díky tomu je výměna dat v rámci spojení WebSocket rychlá. WebSockets se často používají pro aplikace v reálném čase, jako je živý chat nebo hraní her.

Napište server WebSocket

Servery WebSocket v Cloudflare Workers vám umožňují přijímat zprávy od klienta v reálném čase. Tento návod vám ukáže, jak nastavit server WebSocket ve Workers.

Klient může v prohlížeči vytvořit požadavek WebSocket vytvořením nové instance WebSocket, přičemž předáte URL adresu své funkce Workers:

// In client-side JavaScript, connect to your Workers function using WebSockets:
const websocket = new WebSocket(
	"wss://example-websocket.signalnerve.workers.dev",
);

Když příchozí požadavek WebSocket dorazí k funkci Workers, bude obsahovat Upgrade hlavičku, nastavenou na řetězcovou hodnotu websocket. Před vytvořením instance WebSocketu zkontrolujte přítomnost této hlavičky:

async function handleRequest(request) {
  const upgradeHeader = request.headers.get('Upgrade');
  if (!upgradeHeader || upgradeHeader !== 'websocket') {
    return new Response('Expected Upgrade: websocket', { status: 426 });
  }
}
use worker::*;

#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
    let upgrade_header = match req.headers().get("Upgrade") {
        Some(h) => h.to_str().unwrap(),
        None => "",
    };
    if upgrade_header != "websocket" {
        return worker::Response::error("Expected Upgrade: websocket", 426);
    }
}

Jakmile řádně zkontrolujete Upgrade hlavičky můžete vytvořit novou instanci WebSocketPair, který obsahuje serverový a klientský WebSocket. Jeden z těchto WebSocketů by měla obsloužit funkce Workers a druhý by měl být vrácen jako součást Response hodnotou 101 stavový kód, což znamená, že požadavek přepíná protokoly:

async function handleRequest(request) {
  const upgradeHeader = request.headers.get('Upgrade');
  if (!upgradeHeader || upgradeHeader !== 'websocket') {
    return new Response('Expected Upgrade: websocket', { status: 426 });
  }

  const webSocketPair = new WebSocketPair();
  const client = webSocketPair[0],
    server = webSocketPair[1];

  return new Response(null, {
    status: 101,
    webSocket: client,
  });
}
use worker::*;

#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
    let upgrade_header = match req.headers().get("Upgrade") {
        Some(h) => h.to_str().unwrap(),
        None => "",
    };
    if upgrade_header != "websocket" {
        return worker::Response::error("Expected Upgrade: websocket", 426);
    }

    let ws = WebSocketPair::new()?;
    let client = ws.client;
    let server = ws.server;
    server.accept()?;

    worker::Response::from_websocket(client)

}

WebSocketPair konstruktor vrací objekt s 0 a 1 klíčů, z nichž každý obsahuje WebSocket instance jako svou hodnotu. Oba WebSockety z tohoto páru se běžně získávají pomocí Object.values a Destrukturalizace ES6, jak je vidět v příkladu níže.

Aby bylo možné zahájit komunikaci s client WebSocket ve vašem Workeru zavolejte accept na server WebSocket. Tím dáte runtime prostředí Workers pokyn, aby naslouchalo datům WebSocket a udrželo spojení s vaším client WebSocket:

async function handleRequest(request) {
  const upgradeHeader = request.headers.get('Upgrade');
  if (!upgradeHeader || upgradeHeader !== 'websocket') {
    return new Response('Expected Upgrade: websocket', { status: 426 });
  }

  const webSocketPair = new WebSocketPair();
  const [client, server] = Object.values(webSocketPair);

  server.accept();

  return new Response(null, {
    status: 101,
    webSocket: client,
  });
}
use worker::*;

#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
    let upgrade_header = match req.headers().get("Upgrade") {
        Some(h) => h.to_str().unwrap(),
        None => "",
    };
    if upgrade_header != "websocket" {
        return worker::Response::error("Expected Upgrade: websocket", 426);
    }

    let ws = WebSocketPair::new()?;
    let client = ws.client;
    let server = ws.server;
    server.accept()?;

    worker::Response::from_websocket(client)

}

WebSockets vysílají řadu Události ke kterému se lze připojit pomocí addEventListener. Následující příklad se napojuje na message událost a vyvolá console.log daty z něj:

async function handleRequest(request) {
  const upgradeHeader = request.headers.get('Upgrade');
  if (!upgradeHeader || upgradeHeader !== 'websocket') {
    return new Response('Expected Upgrade: websocket', { status: 426 });
  }

  const webSocketPair = new WebSocketPair();
  const [client, server] = Object.values(webSocketPair);

  server.accept();
  server.addEventListener('message', event => {
    console.log(event.data);
  });

  return new Response(null, {
    status: 101,
    webSocket: client,
  });
}
use futures::StreamExt;
use worker::*;

#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
    let upgrade_header = match req.headers().get("Upgrade") {
        Some(h) => h.to_str().unwrap(),
        None => "",
    };
    if upgrade_header != "websocket" {
        return worker::Response::error("Expected Upgrade: websocket", 426);
    }

    let ws = WebSocketPair::new()?;
    let client = ws.client;
    let server = ws.server;
    server.accept()?;

    wasm_bindgen_futures::spawn_local(async move {
        let mut event_stream = server.events().expect("could not open stream");
        while let Some(event) = event_stream.next().await {
            match event.expect("received error in websocket") {
                WebsocketEvent::Message(msg) => server.send(&msg.text()).unwrap(),
                WebsocketEvent::Close(event) => console_log!("{:?}", event),
            }
        }
    });
    worker::Response::from_websocket(client)

}
import { Hono } from 'hono'
import { upgradeWebSocket } from 'hono/cloudflare-workers'

const app = new Hono()

app.get(
  '*',
  upgradeWebSocket((c) => {
    return {
      onMessage(event, ws) {
        console.log('Received message from client:', event.data)
        ws.send(`Echo: ${event.data}`)
      },
      onClose: () => {
        console.log('WebSocket closed:', event)
      },
      onError: () => {
        console.error('WebSocket error:', event)
      },
    }
  })
)

export default app;

Připojte se k serveru WebSocket z klienta

Psaní klientů WebSocket, kteří komunikují s vaší funkcí Workers, je dvoukrokový proces: nejprve vytvoříte instanci WebSocket a poté k ní připojíte posluchače událostí:

const websocket = new WebSocket(
	"wss://websocket-example.signalnerve.workers.dev",
);
websocket.addEventListener("message", (event) => {
	console.log("Message received from server");
	console.log(event.data);
});

Klienti WebSocket mohou odesílat zprávy zpět na server pomocí send funkce:

websocket.send("MESSAGE");

Jakmile je interakce přes WebSocket dokončena, klient může spojení ukončit pomocí close:

websocket.close();

Příklad z praxe najdete v websocket-template abyste začali pracovat s WebSockets.

Napište klienta WebSocket

Cloudflare Workers podporuje new WebSocket(url) konstruktor. Worker může navázat WebSocket připojení ke vzdálenému serveru stejným způsobem, jaký je popsán výše u klientské implementace.

Cloudflare navíc podporuje navazování připojení WebSocket odesláním požadavku fetch na URL adresu s Upgrade hlavičku nastavenou.

async function websocket(url) {
	// Make a fetch request including `Upgrade: websocket` header.
	// The Workers Runtime will automatically handle other requirements
	// of the WebSocket protocol, like the Sec-WebSocket-Key header.
	let resp = await fetch(url, {
		headers: {
			Upgrade: "websocket",
		},
	});

	// If the WebSocket handshake completed successfully, then the
	// response has a `webSocket` property.
	let ws = resp.webSocket;
	if (!ws) {
		throw new Error("server didn't accept WebSocket");
	}

	// Call accept() to indicate that you'll be handling the socket here
	// in JavaScript, as opposed to returning it on to a client.
	// You can pass { allowHalfOpen: true } if you need to coordinate
	// the close handshake manually (for example, when proxying).
	ws.accept();

	// Now you can send and receive messages like before.
	ws.send("hello");
	ws.addEventListener("message", (msg) => {
		console.log(msg.data);
	});
}

Chování WebSocket při uzavření

S web_socket_auto_reply_to_close příznak kompatibility (ve výchozím nastavení povolen u dat kompatibility od 2026-04-07), běhové prostředí Workers automaticky odpovídá na příchozí rámce Close a přechází readyState na CLOSED před vyvoláním close událost. Nemusíte volat close() ve vašem close obslužnou rutinu události, ale je to bezpečné (volání se tiše ignoruje).

Pokud potřebujete chování s částečně otevřeným spojením (half-open), například pro proxy WebSocketu, předejte { allowHalfOpen: true } na accept(). Vezměte na vědomí, že new WebSocket(url) po nabytí účinnosti tohoto příznaku vždy automaticky odpovídá. Pokud chcete pro klientský WebSocket dosáhnout chování half-open, použijte fetch()-ový vzor uvedený výše a zavolat ws.accept({ allowHalfOpen: true }).

Podrobnosti uvádí Chování WebSocket při uzavření.

Komprese WebSocket

Cloudflare Workers podporuje kompresi WebSocket. Více informací najdete v Komprese WebSocket s dalšími informacemi.