Raw New Markdown
Generating updated version of doc...
Rendered New Markdown
Generating updated version of doc...
---
author: clemensv
ms.service: azure-relay
ms.topic: include
ms.date: 01/24/2026
ms.author: samurp
---
### Create a console application
In Visual Studio, create a new **Console App (.NET Framework)** project.
### Add the Relay NuGet package
1. Right-click the newly created project, and then select **Manage NuGet Packages**.
2. Select **Browse**, and then search for **Microsoft.Azure.Relay**. In the search results, select **Microsoft Azure Relay**.
3. Select **Install** to complete the installation. Close the dialog box.
### Write code to send messages
1. At the top of the Program.cs file, replace the existing `using` statements with the following `using` statements:
```csharp
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Relay;
```
2. Add constants to the `Program` class for the hybrid connection details. Replace the placeholders with the values that you obtained when you created the hybrid connection. Be sure to use the fully qualified namespace name.
```csharp
// replace {RelayNamespace} with the name of your namespace
private const string RelayNamespace = "YOUR-RELAY-NAMESPACE-NAME.servicebus.windows.net";
// replace {HybridConnectionName} with the name of your hybrid connection
private const string ConnectionName = "HYBRID-CONNECTION-NAME";
// replace {SAKKeyName} with the name of your Shared Access Policies key, which is RootManageSharedAccessKey by default
private const string KeyName = "SAS-KEY-NAME";
// replace {SASKey} with the primary key of the namespace you saved earlier
private const string Key = "SAS-KEY-VALUE";
```
3. Add the following method to the `Program` class:
```csharp
private static async Task RunAsync()
{
Console.WriteLine("Enter lines of text to send to the server with ENTER");
// Create a new hybrid connection client.
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key);
var client = new HybridConnectionClient(new Uri(String.Format("sb://{0}/{1}", RelayNamespace, ConnectionName)), tokenProvider);
// Initiate the connection.
var relayConnection = await client.CreateConnectionAsync();
// Run two concurrent loops on the connection. One
// reads input from the console and writes it to the connection
// with a stream writer. The other reads lines of input from the
// connection with a stream reader and writes them to the console.
// Entering a blank line shuts down the write task after
// sending it to the server. The server then cleanly shuts down
// the connection, which terminates the read task.
var reads = Task.Run(async () => {
// Initialize the stream reader over the connection.
var reader = new StreamReader(relayConnection);
var writer = Console.Out;
do
{
// Read a full line of UTF-8 text up to newline.
string line = await reader.ReadLineAsync();
// If the string is empty or null, you are done.
if (String.IsNullOrEmpty(line))
break;
// Write to the console.
await writer.WriteLineAsync(line);
}
while (true);
});
// Read from the console and write to the hybrid connection.
var writes = Task.Run(async () => {
var reader = Console.In;
var writer = new StreamWriter(relayConnection) { AutoFlush = true };
do
{
// Read a line from the console.
string line = await reader.ReadLineAsync();
// Write the line out, also when it's empty.
await writer.WriteLineAsync(line);
// Quit when the line is empty,
if (String.IsNullOrEmpty(line))
break;
}
while (true);
});
// Wait for both tasks to finish.
await Task.WhenAll(reads, writes);
await relayConnection.CloseAsync(CancellationToken.None);
}
```
4. Add the following line of code to the `Main` method in the `Program` class.
```csharp
RunAsync().GetAwaiter().GetResult();
```
The Program.cs should look like this:
```csharp
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Relay;
namespace Client
{
class Program
{
private const string RelayNamespace = "{RelayNamespace}.servicebus.windows.net";
private const string ConnectionName = "{HybridConnectionName}";
private const string KeyName = "{SASKeyName}";
private const string Key = "{SASKey}";
static void Main(string[] args)
{
RunAsync().GetAwaiter().GetResult();
}
private static async Task RunAsync()
{
Console.WriteLine("Enter lines of text to send to the server with ENTER");
// Create a new hybrid connection client.
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key);
var client = new HybridConnectionClient(new Uri(String.Format("sb://{0}/{1}", RelayNamespace, ConnectionName)), tokenProvider);
// Initiate the connection.
var relayConnection = await client.CreateConnectionAsync();
// Run two concurrent loops on the connection. One
// reads input from the console and then writes it to the connection
// with a stream writer. The other reads lines of input from the
// connection with a stream reader and then writes them to the console.
// Entering a blank line shuts down the write task after
// sending it to the server. The server then cleanly shuts down
// the connection, which terminates the read task.
var reads = Task.Run(async () => {
// Initialize the stream reader over the connection.
var reader = new StreamReader(relayConnection);
var writer = Console.Out;
do
{
// Read a full line of UTF-8 text up to newline.
string line = await reader.ReadLineAsync();
// If the string is empty or null, you are done.
if (String.IsNullOrEmpty(line))
break;
// Write to the console.
await writer.WriteLineAsync(line);
}
while (true);
});
// Read from the console and write to the hybrid connection.
var writes = Task.Run(async () => {
var reader = Console.In;
var writer = new StreamWriter(relayConnection) { AutoFlush = true };
do
{
// Read a line from the console.
string line = await reader.ReadLineAsync();
// Write the line out, also when it's empty.
await writer.WriteLineAsync(line);
// Quit when the line is empty.
if (String.IsNullOrEmpty(line))
break;
}
while (true);
});
// Wait for both tasks to finish.
await Task.WhenAll(reads, writes);
await relayConnection.CloseAsync(CancellationToken.None);
}
}
}
```
> [!NOTE]
> The sample code in this article uses a connection string to authenticate to an Azure Relay namespace to keep the tutorial simple. We recommend that you use Microsoft Entra ID authentication in production environments, rather than using connection strings or shared access signatures, which can be more easily compromised. For detailed information and sample code for using the Microsoft Entra ID authentication, see [Authenticate and authorize an application with Microsoft Entra ID to access Azure Relay entities](../authenticate-application.md) and [Authenticate a managed identity with Microsoft Entra ID to access Azure Relay resources](../authenticate-managed-identity.md).