Secure WSS Connections in Unreal Engine

BlueprintWebSocket Implementation Guide • Download via Fab Marketplace

Overview

BlueprintWebSocket is an advanced asynchronous networking plugin for Unreal Engine. It enables real-time duplex data transfers with external servers using the WebSocket network architecture, specifically optimized for establishing production-grade TLS/SSL handshake configurations over secure network layers requiring strict authorization.

Download the plugin from the Fab Marketplace to initialize real-time networking elements in your current workspace.

Blueprints

Connecting to a WebSocket server

Using the latency-helper node, you can connect to an active network socket and run automated execution chains immediately:

Blueprint script layout mapping WebSocket connection using helper node

For custom logic structures, create an explicit socket instance to hook up callback events directly into event graph dispatchers:

Advanced Unreal Engine Blueprint connecting with manual delegate assignments

Sending Data

Blueprint node configuration routing structural string messages to WebSocket buffer

Automatically Reconnect on Error

Handle dropouts automatically by defining retry parameters within your baseline node sequence:

Blueprint networking node group managing connection state recovery loops

C++

Adding the Module

Open your source build configuration file (<ProjectName>.Build.cs) and add the module dependency into your compile instruction lists:

PublicDependencyModuleNames.Add("BlueprintWebSocket");

Include

Expose the interface types by placing the wrapper header in your class file:

#include "BlueprintWebSocketWrapper.h"

Creating a new WebSocket

Instantiate a heap allocation using the plugin factory method:

UBlueprintWebSocket* const WebSocket = UBlueprintWebSocket::CreateWebSocket();

Configuring the WebSocket

Modify transport metadata structures and authentication headers prior to invoking the connection handshake routine:

// Merges the provided map with the current header list.
WebSocket->SetHeaders(const TMap<FString, FString> & InHeaders);

// Adds a pair Key / Value to the list of headers.
WebSocket->AddHeader(const FString & Header, const FString & Value);

// Removes the header from the header list
WebSocket->RemoveHeader(const FString & HeaderToRemove);

Listening to events with Callbacks

Bind functional targets to the asynchronous delegates to receive network responses state modifications safely:

Event Name Signature Description
OnConnectedEvent void Func() Called when we are successfully connected to the WebSocket Server.
OnConnectionErrorEvent void Func(const FString & Error) Called when we failed to connect to the WebSocket server.
OnCloseEvent void Func(int64 StatusCode, const FString & Reason, bool bWasClean) Called when the connection with the server has been closed.
OnMessageEvent void Func(const FString & Message) Called when we received a string message.
OnRawMessageEvent void Func(const TArray<uint8> & Data, int32 BytesRemaining) Called when we received a binary message.
OnMessageSentEvent void Func(const FString & Message) Called just after we sent a message.

Because these hooks utilize Dynamic Multicast Delegates, any bound listener function must carry the UFUNCTION() decoration macro to remain visible within the engine invocation stack:

UCLASS()
class MYGAME_API UMyClass : public UObject
{
    GENERATED_BODY()
public:
    // The function we use to bind the events
    void BindEvents();
private:
    // Callbacks
    UFUNCTION() void OnConnected();
    UFUNCTION() void OnConnectionError(const FString & Error);
    UFUNCTION() void OnClosed(int64 StatusCode, const FString & Reason, bool bWasClean);
    UFUNCTION() void OnMessage(const FString & Message);
    UFUNCTION() void OnRawMessage(const TArray<uint8> & Data, int32 BytesRemaining);
    UFUNCTION() void OnMessageSent(const FString & Message);
private:
    UPROPERTY()
    UBlueprintWebSocket* WebSocket;
};

void UMyClass::BindEvents()
{
    // Bind the events so our functions are called when the event is triggered.
    WebSocket->OnConnectedEvent      .AddDynamic(this, &UMyClass::OnConnected);
    WebSocket->OnConnectionErrorEvent.AddDynamic(this, &UMyClass::OnConnectionError);
    WebSocket->OnCloseEvent          .AddDynamic(this, &UMyClass::OnClosed);
    WebSocket->OnMessageEvent        .AddDynamic(this, &UMyClass::OnMessage);
    WebSocket->OnRawMessageEvent     .AddDynamic(this, &UMyClass::OnRawMessage);
    WebSocket->OnMessageSentEvent    .AddDynamic(this, &UMyClass::OnMessageSent);
}

Notice: It is recommended to bind all events you will use before connecting.

Connecting to the WebSocket Server

Pass the explicit endpoint URI and communication protocol sub-strings directly into the connection processing loop:

WebSocket->Connect(TEXT("wss://secure.yourserver.com:443/"), TEXT("ws"));

Query state frames inline via the functional status checkers:

if (WebSocket->IsConnected())
{
    // We are connected.
}
else
{
    // We are not connected.
}

Notice: You shouldn't rely on IsConnected() to handle connection but on the OnConnectedEvent callback.

Sending Messages

Transmit information downstream using structured strings or direct memory buffers:

  1. void SendMessage(const FString & Message): For passing textual/JSON data formats.
  2. void SendRawMessage(const TArray<uint8> & Message, const bool bIsBinary): For pushing compressed structures or asset files.

Implementation sample:

// The data we want to send, you can get it programmatically.
const FString       StringMessage = TEXT("Hello Server");
const TArray<uint8> BinaryMessage = { 0, 1, 2, 3, 4, 5 };

// Send it through our WebSocket.
WebSocket->SendMessage   (StringMessage);
WebSocket->SendRawMessage(BinaryMessage);

Full Example

MyClass.h

#pragma once

#include "CoreMinimal.h"
#include "MyClass.generated.h"

class UBlueprintWebSocket;

/**
 * Our custom class that uses a WebSocket.
 **/
UCLASS()
class MYGAME_API UMyClass : public UObject
{
    GENERATED_BODY()
public:
    void InitializeAndConnectSocket();
private:
    UFUNCTION() 
    void OnConnected();
    UFUNCTION() 
    void OnConnectionError(const FString & Error);
    UFUNCTION() 
    void OnClosed(int64 StatusCode, const FString & Reason, bool bWasClean);
    UFUNCTION() 
    void OnMessage(const FString & Message);
    UFUNCTION()
    void OnRawMessage(const TArray<uint8> & Data, int32 BytesRemaining);
    UFUNCTION()
    void OnMessageSent(const FString & Message);
private:
    UPROPERTY()
    UBlueprintWebSocket* WebSocket;
};

MyClass.cpp

#include "MyClass.h"
#include "BlueprintWebSocketWrapper.h"

void UMyClass::InitializeAndConnectSocket()
{
    WebSocket = UBlueprintWebSocket::CreateWebSocket();
    
    WebSocket->OnConnectedEvent      .AddDynamic(this, &UMyClass::OnConnected);
    WebSocket->OnConnectionErrorEvent.AddDynamic(this, &UMyClass::OnConnectionError);
    WebSocket->OnCloseEvent          .AddDynamic(this, &UMyClass::OnClosed);
    WebSocket->OnMessageEvent        .AddDynamic(this, &UMyClass::OnMessage);
    WebSocket->OnRawMessageEvent     .AddDynamic(this, &UMyClass::OnRawMessage);
    WebSocket->OnMessageSentEvent    .AddDynamic(this, &UMyClass::OnMessageSent);

    WebSocket->AddHeader(TEXT("SomeHeader"), TEXT("SomeValue"));
    WebSocket->Connect(TEXT("wss://secure.yourserver.com:443/"), TEXT("ws"));
}

void UMyClass::OnConnected()
{
    UE_LOG(LogTemp, Log, TEXT("We are connected!"));
    WebSocket->SendMessage(TEXT("Hello Server!"));
}

void UMyClass::OnConnectionError(const FString & Error)
{
    UE_LOG(LogTemp, Error, TEXT("Failed to connect: %s."), *Error);
}

void UMyClass::OnClosed(int64 StatusCode, const FString & Reason, bool bWasClean)
{
    UE_LOG(LogTemp, Warning, TEXT("Connection closed: %d:%s. Clean: %d"), StatusCode, *Reason, bWasClean);
}

void UMyClass::OnMessage(const FString & Message)
{
    UE_LOG(LogTemp, Log, TEXT("New message: %s"), *Message);
}

void UMyClass::OnRawMessage(const TArray<uint8> & Data, int32 BytesRemaining)
{
    UE_LOG(LogTemp, Log, TEXT("New binary message: %d bytes and %d bytes remaining."), Data.Num(), BytesRemaining);
}

void UMyClass::OnMessageSent(const FString & Message)
{
    UE_LOG(LogTemp, Log, TEXT("We just sent %s to the server."), *Message);
}

Troubleshooting

Failed to connect: SSL error: unable to get local issuer certificate

This network security issue arises because public validation root authorities are omitted from the platform bundle inside fully cooked game distributions. Resolve the distribution package integrity via these steps:

  1. Stage your active authorization certificate authority file (.pem format) to your project source path: <Project>/Content/Certificates/cacert.pem.
  2. Expose the target subdirectory to the platform packager via Project Settings > Packaging > Additional Non-Assets Directories To Copy list arrays.

Support

For feature recommendations or functional support updates, open a developer request ticket directly with our core engineering desk at: pandores.marketplace@gmail.com.