---
title: "Writing your first IceRPC + Slice server in C#"
description: "Learn how to write your first IceRPC + Slice server."
url: https://docs.icerpc.dev/getting-started/icerpc-slice-tutorial/server-tutorial
docs_index: https://docs.icerpc.dev/llms.txt
---

> For an index of every page, see [llms.txt](https://docs.icerpc.dev/llms.txt).

# Writing your first IceRPC + Slice server in C#

Learn how to write your first IceRPC + Slice server.

This tutorial is the first part of a two part series that shows how to create a complete application with IceRPC for C#. We start from scratch—you just need to have the .NET 10 SDK installed on your computer.

The networked application we are building together consists of:

- a server that hosts a single service (a simple greeter service)
- a client that establishes a connection to the server and calls `greet` on this greeter service

The client and server are console applications that use plain .NET (no ASP.NET, no Dependency Injection).

The first part of this tutorial shows how to create the server. The second part shows how to [create the client](https://docs.icerpc.dev/getting-started/icerpc-slice-tutorial/client-tutorial.md).

Let's jump right in:

## Install the dotnet-new templates for IceRPC

```shell
dotnet new install IceRpc.Templates
```

## Create the server

```shell
dotnet new icerpc-slice-server -o MySliceServer
```

This command creates a new IceRPC server application in directory `MySliceServer`.

![MySliceServer in Visual Studio Code](https://docs.icerpc.dev/images/MySliceServer.png)

Let's examine each file:

### slice/VisitorCenter/Greeter.slice - the contract

This file holds the contract between our client and server applications, specified with the [Slice](https://docs.icerpc.dev/slice.md) language.

It's a simple greeter:

```slice
[cs::identifier("MySliceServer")]
module VisitorCenter

/// Represents a simple greeter.
interface Greeter {
    /// Creates a personalized greeting.
    /// @param name: The name of the person to greet.
    /// @returns: The greeting.
    greet(name: string) -> string
}
```

The `cs::identifier` attribute instructs the C# code generators to map module `VisitorCenter` to C# namespace `MySliceServer` (our project name) instead of the default (`VisitorCenter`).

If you use this code as the starting point for a new application, you should update this interface to represent something meaningful for your application. For this tutorial, we just keep `Greeter` as-is.

### Chatbot.cs - the service implementation

Class Chatbot is a service that implements Slice interface `Greeter`:

```csharp
[Service]
internal partial class Chatbot : IGreeterService
{
    public ValueTask<string> GreetAsync(
        string name,
        IFeatureCollection features,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"Dispatching greet request {{ name = '{name}' }}");
        return new($"Hello, {name}!");
    }
}
```

The code generator for IceRPC generates C# interface `IGreeterService` from Slice interface `Greeter`. This C# interface is a template: we implement `Greeter` by implementing this interface. As you can see above, the `greet` operation becomes a `GreetAsync` method with two additional parameters.

Since we always fulfill `greet` synchronously, the `GreetAsync` implementation is not marked `async`. We could write the return statement as:

```csharp
    return new ValueTask<string>($"Hello, {name}!");
```

However, it's more convenient to omit the type name, especially when this type is complicated.

We mark class `Chatbot` as partial because the [Service](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.ServiceAttribute.html) attribute instructs the Service Generator (a C# source generator) to implement interface [IDispatcher](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.IDispatcher.html)—in other words, make `Chatbot` an IceRPC service implementation.

### Program.cs - the dispatch pipeline and the server logic

The main program starts by creating and configuring a [Router](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.Router.html):

```csharp
// Create a simple console logger factory and configure the log level for category IceRpc.
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
    builder
        .AddSimpleConsole()
        .AddFilter("IceRpc", LogLevel.Information));

Router router = new Router()
    .UseLogger(loggerFactory)
    .UseDeadline()
    .Map(new Chatbot());
```

This router corresponds to our [dispatch pipeline](https://docs.icerpc.dev/icerpc/dispatch/dispatch-pipeline.md): when we receive a request, we first give it to the [Logger](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.Logger.html) middleware, then to the [Deadline](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.Deadline.html) middleware and finally we route this request based on its path.

The `Map` call means if the request's path is the default service path for the `Chatbot` instance, route it to this `Chatbot` instance. Otherwise, the router returns a response with status code [NotFound](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.StatusCode.html#NotFound).

The default service path for the `Chatbot` instance is `/VisitorCenter.Greeter` because `Chatbot` implements a single Slice interface, `Greeter`. The `Map` call above is a shortcut for:

```csharp
.Map("/VisitorCenter.Greeter", new Chatbot());
```

The main program then creates a [Server](https://code.icerpc.dev/csharp/0.6.x/api/reference/IceRpc.Server.html) that directs all incoming requests to `router`:

```csharp
using X509Certificate2 serverCertificate = X509CertificateLoader.LoadPkcs12FromFile(
    "certs/server.p12",
    password: null,
    keyStorageFlags: X509KeyStorageFlags.Exportable);

await using var server = new Server(
    dispatcher: router,
    serverAuthenticationOptions: CreateServerAuthenticationOptions(serverCertificate),
    logger: loggerFactory.CreateLogger<Server>());
```

We don't specify a server address so this server uses the default server address (`icerpc://[::0]`). This means the new server uses the `icerpc` protocol and will listen for connections on all network interfaces with the default port for `icerpc` (4062).

We don't specify a transport either so we use the default multiplexed transport (`quic`). Setting `serverAuthenticationOptions` is required with `quic`.

At this point, the server is created but is not doing anything yet. A client attempting to connect would get a "connection refused" error.

The server starts listening on the next line:

```csharp
server.Listen();
```

`Listen` returns as soon as the server is listening (so almost immediately).

The main program then awaits until it receives a Ctrl+C. After it receives Ctrl+C, it shuts down the server gracefully:

```csharp
await CancelKeyPressed;
await server.ShutdownAsync();
```

### Program.Authentication.cs - AuthenticationOptions helper

This file contains the `CreateServerAuthenticationOptions` helper method. It creates an `SslServerAuthenticationOptions` from an `X509Certificate2`.

### Program.CancelKeyPressed.cs - small Ctrl+C helper

This file contains a few lines of code that `Program.cs` uses to wait for Ctrl+C. It's not related to RPCs.

### MySliceServer.csproj - the project file

The project file is straightforward. It contains references to the required IceRpc NuGet packages:

- [IceRpc.Slice](https://www.nuget.org/packages/IceRpc.Slice) - the IceRPC + Slice integration package
- [IceRpc.Slice.Tools](https://www.nuget.org/packages/IceRpc.Slice.Tools) - the package that compiles `Greeter.slice` into `generated/Greeter.cs` and `generated/Greeter.IceRpc.cs`
- [IceRpc.Deadline](https://www.nuget.org/packages/IceRpc.Deadline) and [IceRpc.Logger](https://www.nuget.org/packages/IceRpc.Logger) - the packages with the two middleware we installed in the dispatch pipeline

## Run the application

### Start the server

```shell
cd MySliceServer
dotnet run
```

The server is now listening for new connections from clients.

### Shutdown the server

Press Ctrl+C on the server console to shut it down.
