RTI Connext C# API Version 7.3.0
Request-Reply Examples

How to use the C# Request-Reply API.

How to use the C# Request-Reply API.

Requesters and Repliers provide a way to use the Request-Reply communication pattern on top of the DDS entities. An application uses a Requester to send requests to a Replier; another application using a Replier receives a request and can send one or more replies for that request. The Requester that sent the request (and only that one) will receive the reply (or replies).

Note
To use the C# Request-Reply API, add the following NuGet package to your C# project: Rti.ConnextDds.RequestReply

RTI Connext uses DDS data types for sending and receiving requests and replies. Valid types are those generated by the rtiddsgen code generator, the DDS built-in types, and DynamicData. Refer to the User's Manual and the Modern C++ Request-Reply Examples for more information.

The rticonnextdds-examples GitHub repository also provides a full example application using Request-Reply.

Sections:

The IDL definitions of the RandomNumberRequest and RandomNumberReply types used in the examples are the following:

module Example {
struct RandomNumberRequest {
int minValue;
int maxValue;
};
struct RandomNumberReply {
int value;
};
};
Definition: MyType.cs:17

Creation of a Requester and a Replier

Example: Create a basic Requester

// Create a DomainParticipant
using Rti.Dds.Domain.DomainParticipant participant =
factory.CreateParticipant(domainId: 0);
// The only property that is required for a Requester
// is ServiceName. You can alternatively use WithTopicNames.
Requester<RandomNumberRequest, RandomNumberReply> requester =
participant.BuildRequester<RandomNumberRequest, RandomNumberReply>()
.WithServiceName("RandomNumber")
.Create();
Singleton that manages the creation of DomainParticipant objects.
Definition: DomainParticipantFactory.cs:25
static DomainParticipantFactory Instance
Gets the singleton instance of this class.
Definition: DomainParticipantFactory.cs:39
Container for all other Entity objects.
Definition: DomainParticipant.cs:39
Contains DomainParticipant and related classes.
Definition: DomainParticipant.cs:33
Contains the RTI Connext DDS C# API.
Definition: AsyncWaitSetProperty.cs:18
Contains the RTI Connext C# API.
Definition: Logger.cs:20

Example: Create a basic Replier

// Create a DomainParticipant
using Rti.Dds.Domain.DomainParticipant participant =
factory.CreateParticipant(domainId: 0);
// The only property that is required for a Replier
// is ServiceName. You can alternatively use WithTopicNames.
var replier = participant.BuildReplier<RandomNumberRequest, RandomNumberReply>()
.WithServiceName("RandomNumber")
.Create();

Example: Configure a Requester or Replier with additional properties

// Explicitly create a publisher for the requester
var publisher = participant.CreatePublisher();
// Define DataReader and DataWriter QoS
var readerQos = participant.DefaultDataReaderQos
.WithReliability(policy => policy.Kind = ReliabilityKind.Reliable)
.WithHistory(policy => policy.Kind = HistoryKind.KeepAll)
.WithResourceLimits(policy => policy.MaxSamples = LengthUnlimited.Value);
var writerQos = participant.DefaultDataWriterQos
.WithReliability(policy => policy.Kind = ReliabilityKind.Reliable)
.WithHistory(policy => policy.Kind = HistoryKind.KeepAll)
.WithResourceLimits(policy => policy.MaxSamples = 10);
var requester = participant.BuildRequester<RandomNumberRequest, RandomNumberReply>()
.WithServiceName("RandomNumber")
.WithPublisher(publisher)
.WithDataReaderQos(readerQos)
.WithDataWriterQos(writerQos)
.Create();
ReliabilityKind
Kinds of reliability
Definition: Reliability.cs:190
HistoryKind
History kinds
Definition: History.cs:167

Communication between a Requester and a Replier

Example: Send requests

// Send a request
var request = new RandomNumberRequest(10, 1000);
requester.SendRequest(request);
// Receive a reply (wait for it and take the sample)
using var replies = requester.ReceiveReplies(1, Duration.FromSeconds(5));
foreach (var reply in replies.ValidData())
{
System.Console.WriteLine($"Reply received: {reply.Value}");
}

Example: Reply to received requests

// Wait for at least 2 requests asynchronously
// Note: a synchronous version of this wait method also exists
int receivedRequests = await replier.WaitForRequestsAsync(2, cancellationToken);
if (receivedRequests >= 2)
{
LoanedSamples<RandomNumberRequest> requests = replier.TakeRequests();
var randomNumber = new System.Random();
foreach (var request in requests)
{
if (request.Info.ValidData)
{
var reply = new RandomNumberReply(
randomNumber.Next(request.Data.MinValue, request.Data.MaxValue));
// Send a reply for each request. Each reply uses a specific
// request's information to correlate each request and its replies.
replier.SendReply(reply, request.Info);
// Note: a replier can send more than one reply for the same request
// replier.SendReply(anotherRandomNumberRequest, request.Info);
}
}
}
// ...

Example: Correlate requests and their respective replies

// Send requests and save their id's
var request1 = new RandomNumberRequest(0, 100);
var request2 = new RandomNumberRequest(200, 300);
SampleIdentity requestId1 = requester.SendRequest(request1);
SampleIdentity requestId2 = requester.SendRequest(request2);
// Wait for a reply to request2
if (!requester.WaitForReplies(1, requestId2, Duration.FromSeconds(5)))
{
System.Console.WriteLine("No reply for request 2");
}
else
{
// Take replies for request2
using var replies = requester.TakeReplies(requestId2);
foreach (var reply in replies)
{
// This should always be true
System.Diagnostics.Debug.Assert(
request2.Equals(reply.Info.OriginalPublicationVirtualSampleIdentity));
}
// ...
}

Example: Use a handler to process available requests

// Create a handler for incoming requests in the replier
static void customHandler(Replier<RandomNumberRequest, RandomNumberReply> replier)
{
// You can take all currently available requests
using var requests = replier.TakeRequests();
var randomNumber = new System.Random();
foreach (var request in requests)
{
if (request.Info.ValidData)
{
var randomNumberRequest = new RandomNumberReply(
randomNumber.Next(request.Data.MinValue, request.Data.MaxValue));
replier.SendReply(randomNumberRequest, request.Info);
}
}
}
// Add a handler to the RequestsAvailable event. This handler
// will be triggered whenever the replier has available requests
replier.RequestsAvailable += customHandler;
// ...
// Added handlers can be removed from the event
replier.RequestsAvailable -= customHandler;

Using ContentFilteredTopics to filter requests

Example: Create a ContentFilteredTopic and make the Replier use it for requests

// Create a Topic for requests
var requestTopic = participant.CreateTopic<RandomNumberRequest>("RequestsTopic");
// Create a ContentFilteredTopic using the request topic to filter
// the requests using a specific Filter
var cft = participant.CreateContentFilteredTopic(
"RequestsCft",
requestTopic,
new Filter("minvalue > 100"));
// Use the regular topic's name for the requester
var requester =
participant.BuildRequester<RandomNumberRequest, RandomNumberReply>()
.WithTopicNames("RequestsTopic", "RepliesTopic")
.Create();
// Use the name of the CFT as the request topic name for the replier
var filteringReplier =
participant.BuildReplier<RandomNumberRequest, RandomNumberReply>()
.WithTopicNames("RequestsCft", "RepliesTopic")
.Create();
// This request will not be received by filteringReplier
requester.SendRequest(new RandomNumberRequest(50, 100));
// This request will be received by filteringReplier
requester.SendRequest(new RandomNumberRequest(150, 200));
using var requests = filteringReplier.ReceiveRequests(1, Duration.FromSeconds(5));
// ...

Using DynamicData

This example assumes that you have the DynamicType definition of the request type (randomNumberRequestType). See also DynamicData examples to see the different ways to create or get a DynamicType.

Example: Use DynamicData

// Create requester specifying the DynamicType that will be used
// for the requests and replies
var requester = participant.BuildRequester<DynamicData, RandomNumberReply>()
.WithServiceName("RandomNumber")
.WithRequestDynamicType(randomNumberRequestType)
.Create();
var request = new DynamicData(randomNumberRequestType);
request.SetValue("MinValue", 10);
request.SetValue("MaxValue", 100);
requester.SendRequest(request);
// ...

Asynchronous Wait

Example: Wait for samples asynchronously

// Wait for 1 or more requests asynchronously. The wait will stop when
// either the cancellationToken stops it, or when the same or more than
// the specified number of samples are received
var count = await replier.WaitForRequestsAsync(1, cancellationToken);
if (count == 0)
{
throw new System.TimeoutException("No requests received");
}
// Take received requests after waiting for them
using var requests = replier.TakeRequests();
foreach (var request in requests)
{
if (request.Info.ValidData)
{
replier.SendReply(
new RandomNumberReply(new System.Random().Next()),
request.Info);
}
}
// ...

Using a SimpleReplier

Example: Use a SimpleReplier instead of a regular Replier for simplified request handling

// Create a SimpleReplier with a request handler that receives
// a request and builds a reply accordingly. The SimpleReplier will use
// this to reply to each incoming individual request
var simpleReplier =
participant.BuildSimpleReplier<RandomNumberRequest, RandomNumberReply>()
.WithServiceName("SimpleReplierExample")
.WithRequestHandler(
request =>
{
var randomNumber = new System.Random().Next(
request.MaxValue,
request.MinValue);
return new RandomNumberReply(randomNumber);
})
.Create();
// ...