Sample build from scratch of a gRPC client-server application with C# and .NET Core.
Infrastructure
Here’s the folder structure:
- Root folder
GrpcServer(.NET Core Console App)GrpcClient(.NET Core Console App)Message(.NET Core Class Library)gencert.bat(script to generate SSL certificates)
Install OpenSSL
We’ll use OpenSSL to generate self-signed certificates. On Windows, download the installer from slproweb.com/products/Win32OpenSSL.html. Note the install path — you’ll need it in the next step.
Bash script to generate self-signed SSL certificates
This StackOverflow answer contains a batch script for generating the certificates. Save it as gencert.bat and update the OPENSSL_CONF path on line 3 to match your OpenSSL install location:
REM original material from Stack Overflow (https://stackoverflow.com/a/37739265/6089658)
@echo off
set OPENSSL_CONF=C:\Program Files\OpenSSL-Win64\bin\openssl.cfg
echo Generate CA key:
openssl genrsa -passout pass:1111 -des3 -out ca.key 4096
echo Generate CA certificate:
openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=MyRootCA"
echo Generate server key:
openssl genrsa -passout pass:1111 -des3 -out server.key 4096
echo Generate server signing request:
openssl req -passin pass:1111 -new -key server.key -out server.csr -subj "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=%COMPUTERNAME%"
echo Self-sign server certificate:
openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
echo Remove passphrase from server key:
openssl rsa -passin pass:1111 -in server.key -out server.key
echo Generate client key:
openssl genrsa -passout pass:1111 -des3 -out client.key 4096
echo Generate client signing request:
openssl req -passin pass:1111 -new -key client.key -out client.csr -subj "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=%CLIENT-COMPUTERNAME%"
echo Self-sign client certificate:
openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
echo Remove passphrase from client key:
openssl rsa -passin pass:1111 -in client.key -out client.key
Code for this sample project
Generate certificates
Open a PowerShell window in the same folder as gencert.bat and execute: .\gencert.bat
You should now have 8 new files:
- Two for the certificate authority
- Three for the client certificate
- Three for the server certificate
Visual Studio solution
Create a new Visual Studio solution in the root folder and add these .NET Core projects:
Message(Class Library)GrpcServer(Console App)GrpcClient(Console App)
Inside GrpcServer, create a Certs folder and copy the CA and server certificate files there. Set “Copy to output directory” → “Copy if newer” for all files. Repeat for GrpcClient but copy CA and client certificates instead.
Hypothetical example
Chatty microservices is one area where gRPC shines. We’ll take a hypothetical example transferring 500 rows of locations with these properties:
DeviceId→ numberLatitude→ stringLongitude→ string
Protocol Buffers
Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data. We’ll build a messages.proto file to describe the data structure, and Grpc.Tools will generate ready-to-use C# classes from it.
Install Protobuf Language Service in Visual Studio for syntax highlighting.
In the Message project, create messages.proto:
syntax = "proto3";
option csharp_namespace = "Messages";
/* base type for location */
message Location {
int32 deviceId = 1;
string latitude = 2;
string longitude = 3;
}
/* client asking for all locations */
message GetAllRequest {}
/* response with all locations */
message GetAllResponse {
Location location = 1;
}
/* service: InvoiceService has a method GetAll that streams GetAllResponse */
service InvoiceService {
rpc GetAll(GetAllRequest) returns (stream GetAllResponse);
}
Message class library
Install Google.Protobuf, Grpc, and Grpc.Tools NuGet packages. Explicitly include the proto file in Messages.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.7.0" />
<PackageReference Include="Grpc" Version="1.19.0" />
<PackageReference Include="Grpc.Tools" Version="1.19.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<Protobuf Include="messages.proto" />
</ItemGroup>
</Project>
After a project build, the generated C# files appear under .\obj\debug\netcoreapp2.2\ as MessagesGrpc.cs and Messages.cs.
GrpcServer project
Set as startup project, add a reference to Message, and install Google.Protobuf and Grpc NuGet packages.
Create a LocationsService class inheriting from Messages.InvoiceService.InvoiceServiceBase. Override GetAll to stream all entries from a repository:
public class LocationsService : Messages.InvoiceService.InvoiceServiceBase
{
private readonly Repository repository;
public LocationsService()
{
repository = new Repository();
}
public override async Task GetAll(
GetAllRequest request,
IServerStreamWriter<GetAllResponse> responseStream,
ServerCallContext context)
{
foreach (var item in repository.locationsCollection)
{
await responseStream.WriteAsync(new GetAllResponse { Location = item });
}
Console.WriteLine("Finished response stream");
}
}
Add a method to read certificate files, then update Main:
static void Main(string[] args)
{
const int Port = 50050;
const string host = "127.0.0.1";
var servCred = GetCerts(); // reads SslServerCredentials from Certs folder
var server = new Server()
{
Ports = { new ServerPort(host, Port, servCred) },
Services = { Messages.InvoiceService.BindService(new LocationsService()) }
};
server.Start();
Console.WriteLine($"Starting server on port: {Port}\nPress any key to stop");
Console.ReadKey();
server.ShutdownAsync().Wait();
}
GrpcClient project
Install Google.Protobuf and Grpc, add a reference to Message. Set C# version to 7.3 (right-click project → Properties → Build → Advanced → C# version) for async Main support.
Note: the certificates are generated using MachineName, so pay attention to channelOptions:
static async Task Main(string[] args)
{
const int Port = 50050;
const string Host = "127.0.0.1";
var creds = GetSslCredentials();
var channelOptions = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, Environment.MachineName)
};
var channel = new Channel(Host, Port, creds, channelOptions);
var client = new Messages.InvoiceService.InvoiceServiceClient(channel);
Console.WriteLine("GrpcClient ready. Press any key to start");
Console.ReadKey();
using (var call = client.GetAll(new Messages.GetAllRequest()))
{
var responseStream = call.ResponseStream;
while (await responseStream.MoveNext())
{
Console.WriteLine(responseStream.Current.Location);
}
}
Console.WriteLine("\nFinished. Press any key to close");
Console.ReadKey();
}
Test the demo
In Visual Studio: set GrpcServer as startup and hit Debug. Then right-click GrpcClient, choose Debug → Start new instance. You should see the 500 location rows streaming from server to client.
You can find the full source in this GitHub repo.
Conclusion
gRPC shines in many areas. What I like most:
- Performance
- Type safety and intellisense
- Client and server can develop in parallel against the same
.protocontract - Ready-to-use code generation for the top 10 used languages
It comes with caveats too: it’s not intended for browser clients directly, certificates have a cost, and you can’t just curl a test call — you need a proper gRPC client to test it.