Skip to content

endel/NativeWebSocket

Repository files navigation

Native WebSocket

A simple, dependency-free WebSocket client library for Unity, MonoGame, Godot, and any .NET project.

  • No external DLL's required (uses built-in System.Net.WebSockets)
  • WebGL/HTML5 support (Unity)
  • Supports all major build targets
  • Automatic main-thread event dispatching via SynchronizationContext
  • Very simple API

Used in Colyseus Unity SDK.

Installation

Unity

Requires Unity 2019.1+ with .NET 4.x+ Runtime

Note: Do not copy the raw source files from this repository directly into your Unity project. The core WebSocket.cs requires a build-time transformation to add WebGL conditional compilation guards. Use one of the install methods below instead.

Via UPM (Unity Package Manager):

  1. Open Unity
  2. Open Package Manager Window
  3. Click Add Package From Git URL
  4. Enter URL: https://github.com/endel/NativeWebSocket.git#upm-2

If you need the previous 1.x package instead, use https://github.com/endel/NativeWebSocket.git#upm in UPM, or check out the repository sources from the 1.x branch.

Via .unitypackage:

  1. Download NativeWebSocket.unitypackage from the Releases page
  2. In Unity, go to Assets > Import Package > Custom Package and select the downloaded file

MonoGame / .NET

dotnet add package Colyseus.NativeWebSocket
dotnet add package Colyseus.NativeWebSocket.MonoGame

Godot (C#)

dotnet add package Colyseus.NativeWebSocket

Usage

Unity

using UnityEngine;
using NativeWebSocket;

public class Connection : MonoBehaviour
{
    WebSocket websocket;

    async void Start()
    {
        Application.runInBackground = true; // Recommended for WebGL

        websocket = new WebSocket("ws://localhost:3000");

        websocket.OnOpen += () => Debug.Log("Connection open!");
        websocket.OnError += (e) => Debug.Log("Error! " + e);
        websocket.OnClose += (code) => Debug.Log("Connection closed!");

        websocket.OnMessage += (bytes) =>
        {
            var message = System.Text.Encoding.UTF8.GetString(bytes);
            Debug.Log("Received: " + message);
        };

        InvokeRepeating("SendWebSocketMessage", 0.0f, 0.3f);

        await websocket.Connect();
    }

    async void SendWebSocketMessage()
    {
        if (websocket.State == WebSocketState.Open)
        {
            await websocket.Send(new byte[] { 10, 20, 30 });
            await websocket.SendText("plain text message");
        }
    }

    private async void OnApplicationQuit()
    {
        await websocket.Close();
    }
}

WebGL note: Unity pauses the game loop when the browser tab loses focus, which stops all WebSocket send/receive callbacks. To keep the connection active in the background, set Application.runInBackground = true in your script or enable Run In Background in Player Settings > Resolution and Presentation.

MonoGame

Add the WebSocketGameComponent to your game. This installs a SynchronizationContext so all WebSocket events fire on the game thread automatically.

using System;
using System.Text;
using Microsoft.Xna.Framework;
using NativeWebSocket;
using NativeWebSocket.MonoGame;

public class Game1 : Game
{
    private WebSocket _websocket;

    protected override void Initialize()
    {
        Components.Add(new WebSocketGameComponent(this));
        base.Initialize();
    }

    protected override async void LoadContent()
    {
        _websocket = new WebSocket("ws://localhost:3000");

        _websocket.OnOpen += () => Console.WriteLine("Connected!");
        _websocket.OnError += (e) => Console.WriteLine("Error! " + e);
        _websocket.OnClose += (code) => Console.WriteLine("Closed: " + code);

        _websocket.OnMessage += (bytes) =>
        {
            var message = Encoding.UTF8.GetString(bytes);
            Console.WriteLine("Received: (" + bytes.Length + " bytes) " + message);
        };

        await _websocket.Connect();
    }

    protected override async void OnExiting(object sender, EventArgs args)
    {
        if (_websocket != null)
            await _websocket.Close();
        base.OnExiting(sender, args);
    }
}

Godot

Godot Mono has a built-in GodotSynchronizationContext, so no special integration is needed. All WebSocket events fire on the main thread automatically.

using System.Text;
using Godot;
using NativeWebSocket;

public partial class WebSocketExample : Node
{
    private WebSocket _websocket;

    public override async void _Ready()
    {
        _websocket = new WebSocket("ws://localhost:3000");

        _websocket.OnOpen += () => GD.Print("Connected!");
        _websocket.OnError += (e) => GD.Print("Error! " + e);
        _websocket.OnClose += (code) => GD.Print("Closed: " + code);

        _websocket.OnMessage += (bytes) =>
        {
            var message = Encoding.UTF8.GetString(bytes);
            GD.Print("Received: (" + bytes.Length + " bytes) " + message);
        };

        await _websocket.Connect();
    }

    public override void _ExitTree()
    {
        _websocket?.Close();
    }
}

Generic .NET (no SynchronizationContext)

If your environment doesn't have a SynchronizationContext (e.g. a console app), call DispatchMessageQueue() from your main loop to process events:

var ws = new WebSocket("ws://localhost:3000");
ws.OnMessage += (bytes) => Console.WriteLine("Received " + bytes.Length + " bytes");
_ = ws.Connect();

while (true)
{
    ws.DispatchMessageQueue();
    Thread.Sleep(16);
}

Examples

Full runnable examples are in the examples/ directory:

Engine Path How to run
MonoGame examples/MonoGame/ dotnet run --project examples/MonoGame/MonoGameExample.csproj
Godot examples/Godot/ Open in Godot Editor (4.x+ with C#), build, and press Play
Unity examples/Unity/ Import NativeWebSocket via UPM, add Connection.cs to a GameObject

All examples connect to the included test server:

cd node-websocket-server
npm install
npm start

The server listens on ws://localhost:3000, sends periodic text and binary messages, and logs anything received from the client.

API

Constructor

new WebSocket(string url)
new WebSocket(string url, Dictionary<string, string> headers)
new WebSocket(string url, string subprotocol)
new WebSocket(string url, List<string> subprotocols)

Events

Event Signature Description
OnOpen () Connection established
OnMessage (byte[] data) Message received (text or binary)
OnError (string errorMsg) Error occurred
OnClose (WebSocketCloseCode code) Connection closed

Methods

Method Description
Connect() Connect to the server (async)
Close(code, reason) Gracefully close the connection (async)
Send(byte[]) Send binary data (async)
SendText(string) Send text data (async)
CancelConnection() Cancel a pending connection attempt
DispatchMessageQueue() Manually dispatch queued events (only needed without a SynchronizationContext)

Properties

Property Type Description
State WebSocketState Connecting, Open, Closing, or Closed

Migrating from 1.x

Breaking changes

Universal .NET library

The core library no longer depends on UnityEngine. It targets netstandard2.0 and net6.0, and works across Unity, MonoGame, Godot, and any .NET project. Unity-specific code (WebGL) has been moved to separate integration files.

MainThreadUtil, WaitForUpdate, and WaitForBackgroundThread removed

These Unity-specific classes have been removed. Event dispatching is now handled automatically via SynchronizationContext. Remove any references to these classes from your code.

// 1.x β€” these no longer exist in 2.x
MainThreadUtil.Instance
MainThreadUtil.synchronizationContext
new WaitForUpdate()
new WaitForBackgroundThread()

Automatic event dispatching β€” no more Update() dispatch loop

In 1.x, you had to call DispatchMessageQueue() every frame from Update():

// 1.x β€” REQUIRED in Update()
void Update() {
    websocket.DispatchMessageQueue();
}

In 2.x, events are automatically dispatched to the main thread via SynchronizationContext in Unity, Godot, and MonoGame (with WebSocketGameComponent). Remove the Update() dispatch call. DispatchMessageQueue() is only needed in environments without a SynchronizationContext (e.g. console apps).

Close() accepts close code and reason

// 1.x β€” no parameters
await websocket.Close();

// 2.x β€” optional close code and reason
await websocket.Close(WebSocketCloseCode code = WebSocketCloseCode.Normal, string reason = null);

Existing Close() calls without arguments still compile. However, if you implemented the IWebSocket interface directly, you must update your implementation to match the new signature.

IWebSocket interface expanded

The interface now declares methods in addition to events and state:

// 2.x interface
public interface IWebSocket {
    event WebSocketOpenEventHandler OnOpen;
    event WebSocketMessageEventHandler OnMessage;
    event WebSocketErrorEventHandler OnError;
    event WebSocketCloseEventHandler OnClose;

    WebSocketState State { get; }

    // New in 2.x
    Task Connect();
    Task Close(WebSocketCloseCode code = WebSocketCloseCode.Normal, string reason = null);
    Task Send(byte[] data);
    Task SendText(string message);
}

Any custom IWebSocket implementation must now include these methods.

Source files cannot be copied directly

In 1.x, you could copy NativeWebSocket/Assets/WebSocket/WebSocket.cs into your Unity project. In 2.x, the core source lives in src/NativeWebSocket/ and requires a build-time transformation to add WebGL conditional compilation guards. Use UPM or the .unitypackage instead of copying raw files.

Migration checklist

What changed Action required
MainThreadUtil / WaitForUpdate / WaitForBackgroundThread removed Delete any code using these classes
Automatic event dispatching Remove DispatchMessageQueue() from Update() (Unity/Godot/MonoGame)
Custom IWebSocket implementations Add Connect(), Close(), Send(), SendText() methods
Manual file copy installs Switch to UPM or .unitypackage

Acknowledgements

Big thanks to Jiri Hybek. This implementation is based on his work.

License

Apache 2.0

About

πŸ”Œ WebSocket client for Unity/MonoGame/Godot Mono - with no external dependencies (WebGL, Native, Android, iOS, UWP)

Topics

Resources

License

Stars

Watchers

Forks