Skip to content

C# Client Library

The Virtufin WebSocketManager Client library (Virtufin.WebSocketManager.Client) provides a C# interface for managing external WebSocket connections.

Installation

From Gitea NuGet Registry

Add the Virtufin package source with a personal access token (scope: read:packages):

dotnet nuget add source https://nuget.haenerconsulting.com/api/packages/virtufin/nuget/index.json \
  --name Virtufin --username <your-gitea-username> --password <your-gitea-token>

dotnet add package Virtufin.WebSocketManager.Client

Or configure via NuGet.Config:

<configuration>
  <packageSources>
    <add key="Virtufin" value="https://nuget.haenerconsulting.com/api/packages/virtufin/nuget/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <Virtufin>
      <add key="Username" value="<your-gitea-username>" />
      <add key="ClearTextPassword" value="<your-gitea-token>" />
    </Virtufin>
  </packageSourceCredentials>
</configuration>

From Local Source

<ProjectReference Include="path/to/Virtufin.WebSocketManager.Client/Virtufin.WebSocketManager.Client.csproj" />

Overview

The client library provides the WebSocketManagerClient class for connecting to external WebSocket servers through the WebSocketManager service.

Quick Start

using Virtufin.WebSocketManager.Client;
using Virtufin.WebSocketManager.Protos;

using var client = new WebSocketManagerClient("localhost", 5002);

// Connect to a WebSocket server
var connectResponse = await client.ConnectAsync(
    new ConnectRequest { Url = "wss://example.com/socket", AutoReconnect = true });
Console.WriteLine($"Connected: {connectResponse.Id} - {connectResponse.Status}");

// List all connections
var listResponse = await client.ListAsync(new ListRequest());
foreach (var conn in listResponse.Connections)
    Console.WriteLine($"{conn.Id}: {conn.Url} [{conn.Status}]");

// Send a message and wait for response
var sendResponse = await client.SendAsync(new SendRequest {
    Id = connectResponse.Id,
    Message = "{\"action\": \"ping\"}",
    TimeoutMs = 5000
});
Console.WriteLine($"Response: {sendResponse.Response}");

// Disconnect
await client.DisconnectAsync(new DisconnectRequest { Id = connectResponse.Id });

WebSocketManagerClient

The main client class for WebSocket connection management.

Constructor

public WebSocketManagerClient(string host = "localhost", int port = 5002)

Parameters: - host - The gRPC host address (default: "localhost") - port - The gRPC port (default: 5002)

Properties

// Direct access to the underlying gRPC client
public WebSocketManager.WebSocketManagerClient GrpcClient { get; }

Methods

Connection Management

// Connect to a WebSocket server
Task<ConnectResponse> ConnectAsync(ConnectRequest request)

// Disconnect from a WebSocket server
Task<DisconnectResponse> DisconnectAsync(DisconnectRequest request)

// List all managed connections
Task<ListResponse> ListAsync(ListRequest request)

Example:

var response = await client.ConnectAsync(new ConnectRequest {
    Url = "wss://example.com/socket",
    AutoReconnect = true
});
Console.WriteLine($"Connected: {response.Id}");

var connections = await client.ListAsync(new ListRequest());
foreach (var c in connections.Connections)
    Console.WriteLine($"{c.Id}: {c.Url} [{c.Status}]");

await client.DisconnectAsync(new DisconnectRequest { Id = response.Id });

Pub/Sub Integration

// Start publishing WebSocket messages to a Dapr pub/sub topic
Task<StartPublishResponse> StartPublishAsync(StartPublishRequest request)

// Stop publishing to the topic
Task<StopPublishResponse> StopPublishAsync(StopPublishRequest request)

Example:

await client.StartPublishAsync(new StartPublishRequest {
    Id = connectionId,
    Topic = "websocket-events"
});

await client.StopPublishAsync(new StopPublishRequest { Id = connectionId });

Messaging

// Send a message and wait for a correlated response
Task<SendResponse> SendAsync(SendRequest request)

// Send a message without waiting for a response
Task<SendRawResponse> SendRawAsync(SendRawRequest request)

Example:

// Send with correlation (response expected)
var response = await client.SendAsync(new SendRequest {
    Id = connectionId,
    Message = "{\"type\": \"request\", \"id\": \"123\"}",
    TimeoutMs = 5000
});
Console.WriteLine($"Got response: {response.Response}");

// Fire-and-forget
await client.SendRawAsync(new SendRawRequest {
    Id = connectionId,
    Message = "{\"type\": \"notification\"}"
});


Data Models

ConnectRequest

message ConnectRequest {
    string Url = 1;
    bool AutoReconnect = 2;
}

ConnectResponse

message ConnectResponse {
    string Id = 1;
    string Status = 2;
}

WebSocketConnection

message WebSocketConnection {
    string Id = 1;
    string Url = 2;
    string Status = 3;
    string Topic = 4;
    string InstanceId = 5;
}

SendRequest

message SendRequest {
    string Id = 1;
    string Message = 2;
    int32 TimeoutMs = 3;
}

SendResponse

message SendResponse {
    string Response = 1;
}

Error Handling

Connection Not Found

try {
    await client.DisconnectAsync(new DisconnectRequest { Id = "nonexistent" });
}
catch (Grpc.Core.RpcException ex) when (ex.StatusCode == StatusCode.NotFound)
{
    Console.WriteLine("Connection not found");
}

Send Timeout

try {
    var response = await client.SendAsync(new SendRequest {
        Id = connectionId,
        Message = "{\"action\": \"slow\"}",
        TimeoutMs = 100 // very short timeout
    });
}
catch (Grpc.Core.RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
    Console.WriteLine("Send timed out");
}

Connection Errors

try {
    using var client = new WebSocketManagerClient("unreachable-host", 5001);
    var response = await client.ConnectAsync(new ConnectRequest { Url = "wss://invalid" });
}
catch (Grpc.Core.RpcException ex)
{
    Console.WriteLine($"Connection failed: {ex.Status}");
}

Complete Example

using Grpc.Core;
using Virtufin.WebSocketManager.Client;
using Virtufin.WebSocketManager.Protos;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("WebSocketManager Client Demo");
        Console.WriteLine("==============================\n");

using var client = new WebSocketManagerClient("localhost", 5001);

        // Connect
        Console.WriteLine("Connecting to WebSocket server...");
        var connectResponse = await client.ConnectAsync(new ConnectRequest {
            Url = "wss://echo.websocket.org",
            AutoReconnect = false
        });
        Console.WriteLine($"Connected: {connectResponse.Id} - {connectResponse.Status}");

        // List connections
        Console.WriteLine("\nAll connections:");
        var listResponse = await client.ListAsync(new ListRequest());
        foreach (var c in listResponse.Connections) {
            Console.WriteLine($"  {c.Id}: {c.Url} [{c.Status}]");
            if (!string.IsNullOrEmpty(c.Topic))
                Console.WriteLine($"    Publishing to: {c.Topic}");
        }

        // Send a message
        Console.WriteLine("\nSending message...");
        var sendResponse = await client.SendAsync(new SendRequest {
            Id = connectResponse.Id,
            Message = "Hello, WebSocket server!",
            TimeoutMs = 5000
        });
        Console.WriteLine($"Response: {sendResponse.Response}");

        // Start pub/sub
        Console.WriteLine("\nStarting pub/sub...");
        await client.StartPublishAsync(new StartPublishRequest {
            Id = connectResponse.Id,
            Topic = "ws-events"
        });
        Console.WriteLine("Publishing started");

        // Stop pub/sub
        Console.WriteLine("\nStopping pub/sub...");
        await client.StopPublishAsync(new StopPublishRequest { Id = connectResponse.Id });
        Console.WriteLine("Publishing stopped");

        // Disconnect
        Console.WriteLine("\nDisconnecting...");
        await client.DisconnectAsync(new DisconnectRequest { Id = connectResponse.Id });
        Console.WriteLine("Disconnected");
    }
}

Protobuf Dependencies

The client library requires:

<PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.Net.Client" />
<PackageReference Include="Grpc.Tools" />

The proto file (websocketmanager.proto) is compiled into: - Virtufin.WebSocketManager.Protos.WebSocketManager - Virtufin.WebSocketManager.Protos.WebSocketManagerClient - Request/Response message classes

SDK Reference