WitRPC and Discovery
Published on: 2/28/2025
As discussed in previous articles, WitRPC is inspired by WCF, not necessarily in its internal implementation, but in terms of developer experience and capabilities. One of the key technologies that WCF offered “out of the box” was Discovery. With Discovery, a server could announce its presence on a local network via UDP multicast, allowing clients to learn about available services and decide whether to connect. WitRPC offers a similar feature, enhancing flexibility in dynamic environments.
Server-Side Discovery
To enable Discovery during server creation, simply include the appropriate option when building your WitRPC server. For example:
var server = WitServerBuilder.Build(options =>{ options.WithService(ExampleService); options.WithNamedPipe(address); options.WithEncryption(); options.WithJson(); options.WithDiscovery(); options.WithAccessToken(ACCESS_TOKEN); options.WithTimeout(TimeSpan.FromSeconds(1)); options.WithName(dialog.ServerName); options.WithDescription(dialog.ServerDescription);});By default, a one-time “Hello” message is sent at server startup and a “Goodbye” message when it shuts down. These messages are broadcast using UDP multicast.
You can also customize the Discovery parameters:
Custom Address and Port
options.WithDiscovery(host, port);Periodic Announcements
To continuously remind clients that the server is still available, specify a repeat interval:
options.WithDiscovery(host, port, TimeSpan.FromSeconds(10));or simply:
options.WithDiscovery(TimeSpan.FromSeconds(10));While the server is running, a Discovery message is sent every 10 seconds.
The Discovery message contains critical information such as:
-
The transport type and connection settings (e.g., the named pipe, TCP port, or WebSocket URL).
-
The server’s name and description.
Client-Side Discovery
On the client side, to receive Discovery messages, create a Discovery client:
var DiscoveryClient = WitClientBuilder.Discovery(options =>{ options.WithLogger(Logger); options.WithJson(); options.WithAddress(host, port);});Ensure that you specify the same serializer as on the server (JSON is default). If no address is provided, the default multicast address and port are used.
Next, subscribe to the event that is fired upon receiving a Discovery message:
DiscoveryClient.MessageReceived += OnMessageReceived;Then start listening:
DiscoveryClient.Start();Once a Discovery message is received, build a WitRPC client by selecting the appropriate transport based on the message content:
var client = WitClientBuilder.Build(options =>{ options.WithTransport(SelectedMessage); options.WithEncryption(); options.WithJson(); options.WithAccessToken(ACCESS_TOKEN); options.WithLogger(Logger); options.WithTimeout(TimeSpan.FromSeconds(1));});Here’s an example helper method to configure the transport:
public static WitClientBuilderOptions WithTransport(this WitClientBuilderOptions me, DiscoveryMessage message){ if (message.Data == null) throw new ArgumentOutOfRangeException(nameof(me), me, null);
if (message.IsMemoryMappedFile()) return me.WithMemoryMappedFile(message);
if (message.IsNamedPipe()) return me.WithNamedPipe(message);
if (message.IsTcp()) return me.WithTcp("localhost", message);
if (message.IsWebSocket()) return me.WithWebSocket("ws://localhost", message);
throw new ArgumentOutOfRangeException(nameof(me), me, null);}This modernization retains the clarity, ease-of-use, and robust feature set that WitRPC is known for, bringing seamless and intuitive distributed communication to your applications.
You can find detailed examples of using Discovery with WitRPC on GitHub:
WitRPC Discovery Examples