Setting up a gRPC client
Now, we will add a basic gRPC client that will be able to communicate with our service via gRPC. This will be a basic console application. The process will consist of the following steps:
- Initialize the console application project.
- Add some gRPC client dependencies to the project.
- Add some code to connect to the gRPC client.
Once you've followed these steps, your basic console application will be able to send requests to the gRPC server and receive responses from it.
Initializing the project for the client application
If you are using an IDE, you can add a new project to your solution. The template that you will need is called Console Application or Console Project, depending on which IDE you're using. However, you need to make sure that you don't choose the .NET Framework version of the template, which will be clearly labeled. Likewise, make sure that you select the C# template as the IDE may present you with options for other languages too:
As console application is a very basic application type; there won't be any complex setup options to select while creating the project. You can leave all the default options selected. Let's call our new project BasicGrpcClient
.
If you are using a code editor and CLI instead of a fully-fledged IDE, you can create the project by executing a dotnet CLI command. Please ensure that you execute this command from the folder where the BasicGrpcService
project folder is located. It will create the new project folder at the same level inside of your filesystem that your original project folder is located at. The command will be as follows:
dotnet new console -o BasicGrpcClient
Adding gRPC client components to the application
Now, to make your console application act as a gRPC client, you will need to add some NuGet references to your project. You can do so by executing the following commands inside your BasicGrpcClient
project folder:
dotnet add BasicGrpcClient.csproj package Grpc.Net.Client dotnet add BasicGrpcClient.csproj package Google.Protobuf dotnet add BasicGrpcClient.csproj package Grpc.Tools
Once the packages have been installed, the content of your BasicGrpcClient.csproj
file should be similar to this:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Google.Protobuf" Version="3.17.3" /> <PackageReference Include="Grpc.Net.Client" Version="2.38.0" /> <PackageReference Include="Grpc.Tools" Version="2.38.1"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> </Project>
Next, we will need to add the following section to the project file, which references the proto file we will be using to communicate with the server:
<ItemGroup> <Protobuf Include="Protos\greeter.proto" GrpcServices="Client" /> </ItemGroup>
This will make our project file look like this:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Google.Protobuf" Version="3.17.3" /> <PackageReference Include="Grpc.Net.Client" Version="2.38.0" /> <PackageReference Include="Grpc.Tools" Version="2.38.1"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> <ItemGroup> <Protobuf Include="Protos\greeter.proto" GrpcServices="Client" /> </ItemGroup> </Project>
This is similar to what we've done with the server application. But this time, we are telling our application to only generate code for the client components. This is why we have set the GrpcServices
attribute to Client
.
Because we will be connecting to the server we created previously, we need a proto file with package
, service
, rpc
, and message
definitions that are identical to what we had in our server-side proto file. However, our C# namespace can be different.
Therefore, what you'll need to do next is create a Protos
folder inside your BasicGrpsClient
project folder. Then, you must insert the greeter.proto
file into this folder and ensure it has the following content:
syntax = "proto3"; option csharp_namespace = "BasicGrpcClient"; package greeter; // The greetings manager service definition. service GreetingsManager { // Request the service to generate a greeting message. rpc GenerateGreeting (GreetingRequest) returns (GreetingResponse); } // The request message definition containing the name to be addressed in the greeting message. message GreetingRequest { string name = 1; } // The response message definition containing the greeting text. message GreetingResponse { string greetingMessage = 1; }
Please note that this file is identical to the one we have in our server application project, except for the csharp_namespace
element. This element is used by the gRPC tools inside your specific .NET project and it does not affect compatibility between the server and client versions of the proto file. However, the rest of the elements must be the same for the communication system to recognize that it's meant to be the same interface.
Some differences are tolerated (which we will talk about when we cover API versioning in Chapter 5, How to Apply Versioning to the gRPC API). But the fundamental structure of your standard gRPC element definitions must match.
Applying gRPC client components to the code
In your BasicGrpcClient
project, locate the Program.cs
class and change its content to the following:
using System; using System.Threading.Tasks; using Grpc.Net.Client; namespace BasicGrpcClient { class Program { static async Task Main() { // The port number(5001) must match the port of the gRPC server. using var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new GreetingsManager.GreetingsManagerClient(channel); var reply = await client.GenerateGreetingAsync( new GreetingRequest { Name = "BasicGrpcClient" }); Console.WriteLine("Greeting: " + reply. GreetingMessage); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } } }
Please note that the highlighted URL represents the HTTPS access point to the gRPC server. This will not be available if you are running your software on Mac. The workaround to this is described in the Running a gRPC service on Mac section of this chapter.
This code does the following:
- First, it initializes the gRPC channel for the hardcoded address of
https://localhost:5001
. Please note that this is the same address that we defined in thelaunchSettings.json
file in theBasicGrpcService
project. But in a real commercial application, this will be configurable rather than hardcoded. - Then, it uses this channel to initialize a new instance of the gRPC client that was generated by the
GreetingsManager
service definition in ourgreeter.proto
file. - Next, it calls the
GenerateGreetingAsync
method of the client object with a new instance ofGreetingRequest
that has itsName
property set toBasicGrpcClient
. Please note that it represents theGenerateGreeting
rpc
definition from the proto file, but theAsync
part has been added to the name. This is because, in .NET, each gRPC procedure is represented by synchronous and asynchronous methods on the client side. The async version returns anawaitable
task, so the calling code can be set to do something else while we are waiting for the reply. The synchronous version, which has the same name as the originalrpc
definition but without "Async" at the end, blocks execution of the calling code until the result has been received. - From this call, we receive an instance of
GreetingResponse
. - Then, we read the value of its
GreetingMessage
field and print it in the console. - Finally, the console prompts the user to press any key to exit.
Now, you can launch both of your applications and see how they communicate with each other. It's better to launch the server application first to make sure that it has fully loaded before the client application tries to communicate with it.
The simplest way to launch both applications is to open two instances of the command-line window (cmd, PowerShell, or Terminal, depending on your operating system and your preferences). In one command-line window, navigate to the BasicGrpcService
project folder and execute the following command:
dotnet run
This will build and run the server application for you. Once it's showing the output that indicates that the gRPC server is running, open the other instance of the command-line window, navigate to the BasicGrpcClient
project folder, and execute the same command.
You should receive the following output, which indicates that the client was able to successfully call the method on the server via the network:
Now, if you re-examine the code from your client and your server, you will see that it looks almost as if you are calling the code from inside the same application. And that's precisely what makes gRPC so easy to use.
In both the applications that we covered, you saw how relevant code is automatically generated from proto files. In certain scenarios, it would be useful to know how this mechanism works. This is what we will have a look at now.