Skip to content

Web Socket

Artem Podorozhko edited this page Jul 21, 2026 · 1 revision

Web Socket Server

CefWebSocketServer is the WebUserInterface plugin's local WebSocket server module. It exposes named servers through a UGameInstanceSubsystem, keeps one Unreal server object per name, supports multiple clients, and provides Blueprint and C++ APIs for messaging and lifecycle control.

Source: Source/CefWebSocketServer

Why use it

Use this module when browser JavaScript, an external tool, or another local process needs a live bidirectional connection to Unreal. Typical uses include:

  • sending UI actions from a CEF page to game code;
  • pushing game state, settings, or notifications to one or many browser clients;
  • connecting an editor/debug dashboard to a running Unreal instance;
  • carrying JSON or text during development and switching to binary/protobuf messages later;
  • keeping networking and payload encoding separate from gameplay objects.

The module is designed for local UI and tooling bridges. It is not a replacement for a production internet-facing service, authentication layer, or gameplay replication system. Add your own authorization and message validation before exposing a server beyond a trusted local client.

When to use it

Choose CefWebSocketServer when the connection should be long-lived, two-way, and event-driven. It is a good fit when the browser needs to send commands and receive asynchronous updates without polling an HTTP endpoint.

Use the CefContentHttpServer module instead when the browser only needs request/response access to Unreal assets such as images. Use normal Unreal networking or an external service when the connection must support remote players, authentication, or internet deployment.

How the server works

Each named server has a four-stage pipeline:

socket read -> inbound queue -> decode/handle queue -> send queue -> per-client write queue -> socket write
  • Read receives WebSocket frames and applies message-size and receive-rate limits.
  • Handle decodes the selected payload format and invokes the server/client handlers.
  • Send encodes outbound requests and routes them to a client or to all clients.
  • Write flushes queued frames to each socket in bounded batches.

The pipeline prevents socket I/O from blocking the game thread and exposes queue depth and throughput statistics for diagnosis. Server lifecycle objects live with the UGameInstanceSubsystem, so servers remain available until explicitly stopped or the game instance deinitializes.

Payload formats

Set FCefWebSocketPipelineConfig::InPayloadFormat to one of the built-in formats:

Format Use when
Binary The protocol is already encoded as bytes, such as protobuf.
Utf8String The protocol is plain UTF-8 text.
JsonString Browser-facing messages are JSON strings.
XmlString An existing integration requires XML.
Custom The application owns both encode and decode rules.

JsonString is usually the easiest starting point for a browser UI. Binary or Custom is preferable for compact, schema-driven protocols; pair it with the CefProtobuf and CefDispatch modules when appropriate.

Quick start in Blueprint

  1. Get CefWebSocketSubsystem from the current GameInstance.
  2. Build FCefWebSocketServerCreateOptions:
    • set a unique NameId, such as UiBridge;
    • set RequestedPort to a fixed port, or 0 to let the module choose;
    • set the pipeline payload format, for example JsonString.
  3. Call CreateOrGetServer with a server class and client class. The module returns the server object and a FCefWebSocketServerCreateResult.
  4. Check the result and use BoundPort when the requested port was unavailable and auto-adjustment occurred.
  5. Bind OnClientConnected, OnClientDisconnected, OnServerError, and OnClientError.
  6. Send with SendToClientString, SendToClientBytes, BroadcastString, or BroadcastBytes.
  7. Stop with StopServer(NameId) or StopAllServers() during explicit shutdown.

Each connected client receives a stable ClientId for the lifetime of that connection. Use GetClients, GetClient, and GetStats on the server to inspect connected clients and health.

Quick start in C++

#include "Data/CefWebSocketEnums.h"
#include "Data/CefWebSocketStructs.h"
#include "Server/CefWebSocketClientBase.h"
#include "Server/CefWebSocketServerBase.h"
#include "Subsystems/CefWebSocketSubsystem.h"

void UMyGameInstance::StartUiBridge()
{
    UCefWebSocketSubsystem* subsystem = GetSubsystem<UCefWebSocketSubsystem>();
    if (!subsystem)
    {
        return;
    }

    FCefWebSocketServerCreateOptions options;
    options.NameId = FName(TEXT("UiBridge"));
    options.RequestedPort = 7001;
    options.InPipelineConfig.InPayloadFormat = ECefWebSocketPayloadFormat::JsonString;

    UCefWebSocketServerBase* server = nullptr;
    const FCefWebSocketServerCreateResult result = subsystem->CreateOrGetServer(
        options,
        UCefWebSocketServerBase::StaticClass(),
        UCefWebSocketClientBase::StaticClass(),
        server);

    if (!server || result.Result == ECefWebSocketCreateResult::Failed)
    {
        return;
    }

    server->BroadcastString(TEXT("{\"type\":\"server-online\"}"));
}

The browser can connect to the bound port with a normal WebSocket client:

const socket = new WebSocket("ws://127.0.0.1:7001");
socket.onmessage = (event) => console.log("UE:", event.data);
socket.onopen = () => socket.send(JSON.stringify({ type: "ui-ready" }));

The actual port should come from FCefWebSocketServerCreateResult::BoundPort if port auto-adjustment is allowed.

Handling inbound messages

For a small integration, subclass UCefWebSocketServerBase and override:

  • HandleClientString(UCefWebSocketClientBase*, const FString&);
  • HandleClientBytes(UCefWebSocketClientBase*, const TArray<uint8>&).

You can also subclass UCefWebSocketClientBase and override its HandleStringFromClient or HandleBytesFromClient methods. The base client object provides GetClientId, GetRemoteAddress, SendString, SendBytes, and Disconnect.

The default handlers are empty. The module invokes them from its handling pipeline, not as a general game-thread event dispatcher. If a handler needs to mutate actors, UObjects, widgets, or other game-thread state, copy/validate the message and enqueue a small command to the game thread before touching that state.

Custom codecs

Implement ICefWebSocketPacketCodec when built-in formats are not enough. The codec must provide:

bool DecodeInbound(
    const FCefWebSocketInboundPacket& input,
    FCefWebSocketInboundPacket& decoded,
    FString& error);

bool EncodeSendRequest(
    const FCefWebSocketSendRequest& request,
    TArray<FCefWebSocketWritePacket>& output,
    FString& error);

ECefWebSocketPayloadFormat GetPayloadFormat() const;

Install it with SetPacketCodec from C++. Keep codec ownership and thread safety in mind: encode/decode work happens in the pipeline, so shared mutable state must be protected or avoided.

Sending and lifecycle

Available server operations include:

  • SendToClientString / SendToClientBytes for one client;
  • BroadcastString / BroadcastBytes for every client;
  • BroadcastStringExcept / BroadcastBytesExcept for every client except one;
  • DisconnectClient to close one connection with a reason;
  • StopServer to stop the server and clear its client objects.

CreateOrGetServer is idempotent by NameId: calling it again returns the existing server and does not create a second listener for that name. Requested ports may auto-adjust to the next available port. Treat BoundPort as authoritative.

Errors and backpressure

Send methods return ECefWebSocketSendResult. Important failure cases include InvalidClient, Disconnected, QueueFull, TooLarge, SerializeFailed, and InvalidUtf8.

The pipeline has bounded queues. Monitor GetStats and decide how the application should behave when a queue fills: drop/coalesce transient UI updates, reject a command, or report an error. Do not assume every send is immediately written to the socket.

Relevant limits include:

CVar Purpose
cefws.max_message_bytes Maximum inbound message size.
cefws.max_text_message_bytes Maximum text-frame size.
cefws.max_outbound_message_bytes Maximum outbound payload size.
cefws.max_rx_bytes_per_sec_per_client Per-client receive bandwidth cap.
cefws.max_tx_bytes_per_sec_per_client Per-client send bandwidth cap.
cefws.max_queue_messages_per_client Global per-client outbound message limit.
cefws.max_queue_bytes_per_client Global per-client outbound byte limit.
cefws.queue_drop_policy 0 drops oldest; 1 rejects new messages.
cefws.heartbeat_interval_sec Heartbeat interval; <=0 disables it.
cefws.idle_timeout_sec Client idle timeout; <=0 disables it.
cefws.validate_utf8 Validates text payloads when enabled.
cefws.log_traffic Enables payload-flow logging.

Adjust limits for the application rather than disabling safety checks by default.

Debugging

Use the Unreal console commands:

ws.list
ws.stats UiBridge
ws.kick UiBridge <clientId>
ws.stop UiBridge

ws.list and ws.stats expose active clients, traffic, dropped messages, and queue depths for the inbound, handle, send, and write stages. Start with cefws.log_traffic 1 for a short diagnostic session, then turn it off when finished.

Common mistakes

  • Using a port that is already occupied and ignoring BoundPort after auto-adjustment.
  • Creating multiple servers with different names when one bridge would be sufficient.
  • Calling game-thread-only Unreal APIs directly from a message handler.
  • Sending large or high-frequency updates without considering queue limits.
  • Using JSON for large/high-rate binary data when protobuf or a custom codec would be more appropriate.
  • Exposing the local listener to untrusted clients without authentication and message validation.

Clone this wiki locally