Skip to content

Commit a37cfbe

Browse files
New Bidir demo for C# (#291)
1 parent 8a65ec9 commit a37cfbe

File tree

21 files changed

+276
-410
lines changed

21 files changed

+276
-410
lines changed

csharp/C# NET demos.sln

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "client", "Ice\session\msbui
1010
EndProject
1111
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server", "Ice\session\msbuild\server\server.csproj", "{F0BEAEC8-0D12-4196-A20D-2681EBDA15E0}"
1212
EndProject
13-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bidir", "bidir", "{A1DE7220-B866-4216-A6A1-DA2B0D136A76}"
14-
EndProject
15-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "client", "Ice\bidir\msbuild\client\client.csproj", "{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}"
16-
EndProject
17-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server", "Ice\bidir\msbuild\server\server.csproj", "{46E5DF0C-DBEE-4874-A778-776A3640C0F3}"
18-
EndProject
1913
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "context", "context", "{D04FD855-69A8-43E5-8F94-FB16B3D8BCA8}"
2014
EndProject
2115
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "client", "Ice\context\msbuild\client\client.csproj", "{1A96996B-B60D-473E-98F4-53F1EC589D68}"
@@ -132,30 +126,6 @@ Global
132126
{F0BEAEC8-0D12-4196-A20D-2681EBDA15E0}.Release|x64.Build.0 = Release|Any CPU
133127
{F0BEAEC8-0D12-4196-A20D-2681EBDA15E0}.Release|x86.ActiveCfg = Release|Any CPU
134128
{F0BEAEC8-0D12-4196-A20D-2681EBDA15E0}.Release|x86.Build.0 = Release|Any CPU
135-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
136-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
137-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Debug|x64.ActiveCfg = Debug|Any CPU
138-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Debug|x64.Build.0 = Debug|Any CPU
139-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Debug|x86.ActiveCfg = Debug|Any CPU
140-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Debug|x86.Build.0 = Debug|Any CPU
141-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
142-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Release|Any CPU.Build.0 = Release|Any CPU
143-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Release|x64.ActiveCfg = Release|Any CPU
144-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Release|x64.Build.0 = Release|Any CPU
145-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Release|x86.ActiveCfg = Release|Any CPU
146-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0}.Release|x86.Build.0 = Release|Any CPU
147-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
148-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
149-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Debug|x64.ActiveCfg = Debug|Any CPU
150-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Debug|x64.Build.0 = Debug|Any CPU
151-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Debug|x86.ActiveCfg = Debug|Any CPU
152-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Debug|x86.Build.0 = Debug|Any CPU
153-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
154-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Release|Any CPU.Build.0 = Release|Any CPU
155-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Release|x64.ActiveCfg = Release|Any CPU
156-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Release|x64.Build.0 = Release|Any CPU
157-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Release|x86.ActiveCfg = Release|Any CPU
158-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3}.Release|x86.Build.0 = Release|Any CPU
159129
{1A96996B-B60D-473E-98F4-53F1EC589D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
160130
{1A96996B-B60D-473E-98F4-53F1EC589D68}.Debug|Any CPU.Build.0 = Debug|Any CPU
161131
{1A96996B-B60D-473E-98F4-53F1EC589D68}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -452,9 +422,6 @@ Global
452422
{86DCC48B-624F-441C-A78E-88BB224CF554} = {0B69E55F-3828-41DC-B50D-22A9E7BEE80B}
453423
{E1D29995-B491-4FF2-ADB3-8C6DA3CBA15C} = {86DCC48B-624F-441C-A78E-88BB224CF554}
454424
{F0BEAEC8-0D12-4196-A20D-2681EBDA15E0} = {86DCC48B-624F-441C-A78E-88BB224CF554}
455-
{A1DE7220-B866-4216-A6A1-DA2B0D136A76} = {0B69E55F-3828-41DC-B50D-22A9E7BEE80B}
456-
{ED0B9C9F-D2CF-4EEF-8A4D-5D8D5FC230E0} = {A1DE7220-B866-4216-A6A1-DA2B0D136A76}
457-
{46E5DF0C-DBEE-4874-A778-776A3640C0F3} = {A1DE7220-B866-4216-A6A1-DA2B0D136A76}
458425
{D04FD855-69A8-43E5-8F94-FB16B3D8BCA8} = {0B69E55F-3828-41DC-B50D-22A9E7BEE80B}
459426
{1A96996B-B60D-473E-98F4-53F1EC589D68} = {D04FD855-69A8-43E5-8F94-FB16B3D8BCA8}
460427
{E189473B-221E-41E2-AD04-01A2AA282FC5} = {D04FD855-69A8-43E5-8F94-FB16B3D8BCA8}

csharp/Ice/Bidir/Bidir.sln

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.4.33122.133
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{527EEA4D-77B9-4252-A2CD-C641A25CAD53}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FBDAF448-665A-4595-98D3-4538C56A666D}"
11+
ProjectSection(SolutionItems) = preProject
12+
README.md = README.md
13+
EndProjectSection
14+
EndProject
15+
Global
16+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17+
Debug|Any CPU = Debug|Any CPU
18+
Release|Any CPU = Release|Any CPU
19+
EndGlobalSection
20+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
21+
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22+
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Debug|Any CPU.Build.0 = Debug|Any CPU
23+
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Release|Any CPU.ActiveCfg = Release|Any CPU
24+
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Release|Any CPU.Build.0 = Release|Any CPU
25+
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26+
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Debug|Any CPU.Build.0 = Debug|Any CPU
27+
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Release|Any CPU.ActiveCfg = Release|Any CPU
28+
EndGlobalSection
29+
GlobalSection(SolutionProperties) = preSolution
30+
HideSolutionNode = FALSE
31+
EndGlobalSection
32+
GlobalSection(ExtensibilityGlobals) = postSolution
33+
SolutionGuid = {0B36F1E1-0592-4A15-9981-67BC4A653EC4}
34+
EndGlobalSection
35+
EndGlobal
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<!-- Copy the PDBs from the NuGet packages to get file names and line numbers in stack traces. -->
9+
<CopyDebugSymbolFilesFromPackages>true</CopyDebugSymbolFilesFromPackages>
10+
</PropertyGroup>
11+
<ItemGroup>
12+
<SliceCompile Include="../slice/AlarmClock.ice" />
13+
<PackageReference Include="zeroc.ice.net" Version="3.8.0-alpha0" />
14+
<PackageReference Include="zeroc.icebuilder.msbuild" Version="5.0.9" />
15+
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
16+
<PrivateAssets>all</PrivateAssets>
17+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
18+
</PackageReference>
19+
</ItemGroup>
20+
</Project>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
using EarlyRiser;
4+
5+
namespace Client;
6+
7+
/// <summary>MockAlarmClock is an Ice servant that implements Slice interface AlarmClock.</summary>
8+
internal class MockAlarmClock : AlarmClockDisp_
9+
{
10+
/// <summary>Gets a task that completes when we return <see cref="ButtonPressed.Stop" /> from <see cref="Ring" />.
11+
/// </summary>
12+
public Task StopPressed => _tcs.Task;
13+
14+
// RunContinuationsAsynchronously because we don't want to continue Main in an Ice thread pool thread.
15+
private readonly TaskCompletionSource _tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
16+
17+
private bool _needMoreTime = true;
18+
19+
/// <inheritdoc/>
20+
// Implements the abstract method Ring from the AlarmClockDisp_ class generated by the Slice compiler.
21+
public override ButtonPressed Ring(string message, Ice.Current current)
22+
{
23+
Console.WriteLine($"Dispatching ring request {{ message = '{message}' }}");
24+
if (_needMoreTime)
25+
{
26+
Console.WriteLine($"Returning {ButtonPressed.Snooze} to request more time.");
27+
_needMoreTime = false; // we only snooze one time
28+
return ButtonPressed.Snooze;
29+
}
30+
else
31+
{
32+
_tcs.TrySetResult();
33+
Console.WriteLine($"Returning {ButtonPressed.Stop} to stop the alarm.");
34+
return ButtonPressed.Stop;
35+
}
36+
}
37+
}

csharp/Ice/Bidir/Client/Program.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
// Slice module EarlyRiser in AlarmClock.ice maps to C# namespace EarlyRiser.
4+
using EarlyRiser;
5+
6+
// Create an Ice communicator to initialize the Ice runtime.
7+
using Ice.Communicator communicator = Ice.Util.initialize(ref args);
8+
9+
// Create an object adapter with no name and no configuration. This object adapter does not need to be activated.
10+
Ice.ObjectAdapter adapter = communicator.createObjectAdapter("");
11+
12+
// Sets this object adapter as the default object adapter on the communicator.
13+
communicator.setDefaultObjectAdapter(adapter);
14+
15+
// Register the MockAlarmClock servant with the adapter. The wake up service knows we use identity "alarmClock".
16+
var mockAlarmClock = new Client.MockAlarmClock();
17+
adapter.add(mockAlarmClock, Ice.Util.stringToIdentity("alarmClock"));
18+
19+
// Create a proxy to the wake-up service.
20+
WakeUpServicePrx wakeUpService = WakeUpServicePrxHelper.createProxy(
21+
communicator,
22+
"wakeUpService:tcp -h localhost -p 4061");
23+
24+
// Schedule a wake-up call in 5 seconds. This call establishes the connection to the server; incoming requests over this
25+
// connection are handled by the communicator's default object adapter.
26+
await wakeUpService.WakeMeUpAsync(DateTime.Now.AddSeconds(5).ToUniversalTime().Ticks);
27+
Console.WriteLine("Wake-up call scheduled, falling asleep...");
28+
29+
// Wait until the "stop" button is pressed on the mock alarm clock.
30+
await mockAlarmClock.StopPressed;
31+
Console.WriteLine("Stop button pressed, exiting...");

csharp/Ice/Bidir/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Bidir
2+
3+
The Bidir demo illustrates how to send requests "the other way around", from a server to a client, by reusing the
4+
connection established by the client to the server.
5+
6+
This demo is very similar to the [../Callback] demo: with the Callback demo, the server opens a connection to the
7+
client, while with this demo, there is only one connection, from the client to the server:
8+
9+
```mermaid
10+
flowchart LR
11+
c[Client<br>hosts AlarmClock] --bidir connection--> s[Server:4061<br>hosts WakeUpService]
12+
```
13+
14+
This is particularly useful when the client application is behind a firewall that does not allow incoming connections.
15+
16+
You can build the client and server applications with:
17+
18+
``` shell
19+
dotnet build
20+
```
21+
22+
First start the Server program:
23+
24+
```shell
25+
cd Server
26+
dotnet run
27+
```
28+
29+
In a separate terminal, start the Client program:
30+
31+
```shell
32+
cd Client
33+
dotnet run
34+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
using EarlyRiser;
4+
5+
namespace Server;
6+
7+
/// <summary>BidirWakeUpService is an Ice servant that implements Slice interface WakeUpService.</summary>
8+
internal class BidirWakeUpService : WakeUpServiceDisp_
9+
{
10+
/// <inheritdoc/>
11+
// Implements the abstract method WakeMeUp from the WakeUpServiceDisp_ class generated by the Slice compiler.
12+
public override void WakeMeUp(long timeStamp, Ice.Current current)
13+
{
14+
var timeStampDateTime = new DateTime(timeStamp, DateTimeKind.Utc);
15+
Console.WriteLine($"Dispatching wakeMeUp request {{ timeStamp = '{timeStampDateTime.ToLocalTime()}' }}");
16+
17+
Ice.Connection? connection = current.con; // The connection from the client to the server.
18+
if (connection is null)
19+
{
20+
// Should never happen, but in case it does, the Ice runtime transmits this exception as an
21+
// Ice.UnknownException.
22+
throw new NotImplementedException("BidirWakeUpService does not support collocated calls");
23+
}
24+
25+
// Create a proxy to the client's alarm clock. This connection-bound proxy is called a "fixed proxy".
26+
AlarmClockPrx alarmClock = AlarmClockPrxHelper.uncheckedCast(
27+
connection.createProxy(Ice.Util.stringToIdentity("alarmClock")));
28+
29+
// Start a background task to ring the alarm clock.
30+
_ = Task.Run(async () =>
31+
{
32+
// Wait until the specified time.
33+
TimeSpan delay = timeStampDateTime - DateTime.UtcNow;
34+
if (delay > TimeSpan.Zero)
35+
{
36+
await Task.Delay(delay);
37+
}
38+
39+
// First ring. This invocation reuses the connection established by the client.
40+
ButtonPressed buttonPressed = await alarmClock.RingAsync("It's time to wake up!");
41+
42+
// Keep ringing every 10 seconds until the user presses the stop button.
43+
while (buttonPressed is ButtonPressed.Snooze)
44+
{
45+
await Task.Delay(TimeSpan.FromSeconds(10));
46+
buttonPressed = await alarmClock.RingAsync("No more snoozing!");
47+
}
48+
});
49+
}
50+
}

csharp/Ice/Bidir/Server/Program.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
// Create an Ice communicator to initialize the Ice runtime.
4+
using Ice.Communicator communicator = Ice.Util.initialize(ref args);
5+
6+
// Create an object adapter that listens for incoming requests and dispatches them to servants.
7+
Ice.ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("WakeUpAdapter", "tcp -p 4061");
8+
9+
// Register the BidirWakeUpService servant with the adapter.
10+
adapter.add(new Server.BidirWakeUpService(), Ice.Util.stringToIdentity("wakeUpService"));
11+
12+
// Start dispatching requests.
13+
adapter.activate();
14+
Console.WriteLine("Listening on port 4061...");
15+
16+
// Wait until the user presses Ctrl+C.
17+
await CancelKeyPressed;
18+
Console.WriteLine("Caught Ctrl+C, exiting...");
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<!-- Copy the PDBs from the NuGet packages to get file names and line numbers in stack traces. -->
9+
<CopyDebugSymbolFilesFromPackages>true</CopyDebugSymbolFilesFromPackages>
10+
</PropertyGroup>
11+
<ItemGroup>
12+
<SliceCompile Include="../slice/AlarmClock.ice" />
13+
<PackageReference Include="zeroc.ice.net" Version="3.8.0-alpha0" />
14+
<PackageReference Include="zeroc.icebuilder.msbuild" Version="5.0.9" />
15+
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
16+
<PrivateAssets>all</PrivateAssets>
17+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
18+
</PackageReference>
19+
<Compile Include="../../../Common/Program.CancelKeyPressed.cs" Link="Program.CancelKeyPressed.cs" />
20+
</ItemGroup>
21+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) ZeroC, Inc.
2+
3+
#pragma once
4+
5+
module EarlyRiser
6+
{
7+
/// Indicates the button pressed when the alarm rang.
8+
enum ButtonPressed { Snooze, Stop }
9+
10+
/// Represents a simple alarm clock. It's the "callback" in this demo, and it's implemented by the client.
11+
interface AlarmClock
12+
{
13+
/// Rings the alarm clock.
14+
/// @param message The message to display.
15+
/// @return The button pressed by the user.
16+
["cs:identifier:Ring"] // We prefer PascalCase for C# methods.
17+
ButtonPressed ring(string message);
18+
}
19+
20+
/// Represents the wake up service provided by the server.
21+
interface WakeUpService
22+
{
23+
/// Schedules a call to the caller's {@link AlarmClock::ring} at the specified time.
24+
/// @param timeStamp The time to ring the alarm clock. It's encoded as the number of ticks (100 nanoseconds)
25+
/// since January 1, 0001 00:00:00 UTC in the Gregorian calendar.
26+
["cs:identifier:WakeMeUp"] // We prefer PascalCase for C# methods.
27+
void wakeMeUp(long timeStamp);
28+
}
29+
}

0 commit comments

Comments
 (0)