mirror of https://github.com/grpc/grpc.io.git
637 lines
36 KiB
HTML
637 lines
36 KiB
HTML
|
|
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400" rel="stylesheet">
|
|
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
|
<title>
|
|
gRPC Basics - C# – gRPC
|
|
</title>
|
|
|
|
|
|
<link rel="apple-touch-icon" href="/favicons/apple-touch-icon.png" sizes="180x180">
|
|
<link rel="icon" type="image/png" href="/favicons/android-chrome-192x192.png" sizes="192x192" >
|
|
<link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
|
|
<link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
|
|
<link rel="manifest" href="/favicons/manifest.json">
|
|
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#2DA6B0">
|
|
<meta name="msapplication-TileColor" content="#ffffff">
|
|
<meta name="msapplication-TileImage" content="/favicons/mstile-150x150.png">
|
|
|
|
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-60127042-1"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
|
|
gtag('config', 'UA-60127042-1');
|
|
</script>
|
|
|
|
</head>
|
|
<body>
|
|
<div id="landing-content">
|
|
<div class="row">
|
|
<div class="topbannersub">
|
|
<nav class="navbar navbar-expand-md navbar-dark topnav">
|
|
<a class="navbar-brand" href="https://cjyabraham.github.io/">
|
|
<img src="https://cjyabraham.github.io/img/grpc-logo.png" width="114" height="50">
|
|
</a>
|
|
|
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
|
|
<div class="topnav, collapse navbar-collapse" id="navbarSupportedContent" style="float:right !important">
|
|
<ul class="navbar-nav ml-auto">
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="https://cjyabraham.github.io/about/">About</a>
|
|
</li>
|
|
<li class="nav-item dropdown active">
|
|
<a class="nav-link dropdown-toggle" href="https://cjyabraham.github.io/docs/" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
Docs
|
|
</a>
|
|
|
|
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
|
|
|
|
|
<a class="dropdown-item" href="/docs">
|
|
Overview
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/quickstart/">
|
|
Quick Start
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/guides/">
|
|
Guides
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/tutorials/">
|
|
Tutorials
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/reference/">
|
|
Reference
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/samples/">
|
|
Samples
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/talks">
|
|
Presentations
|
|
</a>
|
|
|
|
</div>
|
|
</li>
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="/blog">
|
|
Blog
|
|
</a>
|
|
</li>
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="/community">Community</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="https://packages.grpc.io/">
|
|
Packages
|
|
</a>
|
|
</li>
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="https://cjyabraham.github.io/faq/">FAQ</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
|
|
<div class="headertext">Documentation</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="subnav d-none d-md-block">
|
|
<a href="https://cjyabraham.github.io/docs/" >Overview</a>
|
|
| <a href="https://cjyabraham.github.io/docs/quickstart/" >Quick Start</a>
|
|
| <a href="https://cjyabraham.github.io/docs/guides/" >Guides</a>
|
|
| <a href="https://cjyabraham.github.io/docs/tutorials/" class="active">Tutorials</a>
|
|
| <a href="https://cjyabraham.github.io/docs/reference/" >Reference</a>
|
|
| <a href="https://cjyabraham.github.io/docs/samples/" >Samples</a>
|
|
| <a href="https://cjyabraham.github.io/docs/talks/" >Presentations</a>
|
|
</div>
|
|
|
|
|
|
<div class="quickstartcols">
|
|
|
|
<div class="quickstartcol1">
|
|
|
|
<h8>Tutorials</h8>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/async/helloasync-cpp/" >Async - C++</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/auth/oauth2-objective-c/" >Auth - Objective C</a>
|
|
<h8>Basic</h8>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/c/" >C++</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/csharp/" class="active">C#</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/dart/" >Dart</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/go/" >Go</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/java/" >Java</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/android/" >Android Java</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/node/" >Node</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/objective-c/" >Objective-C</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/php/" >PHP</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/python/" >Python</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/ruby/" >Ruby</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/web/" >Web</a>
|
|
|
|
</div>
|
|
|
|
<div class="quickstartcol2" style="margin-top:4%">
|
|
<h3 style="margin-top:0px;">gRPC Basics - C#</h3>
|
|
|
|
|
|
|
|
<p>This tutorial provides a basic C# programmer’s introduction to working with gRPC.</p>
|
|
|
|
<p>By walking through this example you’ll learn how to:</p>
|
|
|
|
<ul>
|
|
<li>Define a service in a .proto file.</li>
|
|
<li>Generate server and client code using the protocol buffer compiler.</li>
|
|
<li>Use the C# gRPC API to write a simple client and server for your service.</li>
|
|
</ul>
|
|
|
|
<p>It assumes that you have read the <a href="/docs/">Overview</a> and are familiar
|
|
with <a href="https://developers.google.com/protocol-buffers/docs/overview">protocol buffers</a>. Note that the
|
|
example in this tutorial uses the proto3 version of the protocol buffers
|
|
language: you can find out more in the
|
|
<a href="https://developers.google.com/protocol-buffers/docs/proto3">proto3 language guide</a> and
|
|
<a href="https://developers.google.com/protocol-buffers/docs/reference/csharp-generated">C# generated code reference</a>.</p>
|
|
|
|
<div id="toc"></div>
|
|
|
|
<h3 id="why-use-grpc">Why use gRPC?</h3>
|
|
|
|
<p>Our example is a simple route mapping application that lets clients get
|
|
information about features on their route, create a summary of their route, and
|
|
exchange route information such as traffic updates with the server and other
|
|
clients.</p>
|
|
|
|
<p>With gRPC we can define our service once in a .proto file and implement clients
|
|
and servers in any of gRPC’s supported languages, which in turn can be run in
|
|
environments ranging from servers inside Google to your own tablet - all the
|
|
complexity of communication between different languages and environments is
|
|
handled for you by gRPC. We also get all the advantages of working with protocol
|
|
buffers, including efficient serialization, a simple IDL, and easy interface
|
|
updating.</p>
|
|
|
|
<h3 id="example-code-and-setup">Example code and setup</h3>
|
|
|
|
<p>The example code for our tutorial is in
|
|
<a href="https://github.com/grpc/grpc/tree/
|
|
v1.20.0/examples/csharp/RouteGuide">grpc/grpc/examples/csharp/RouteGuide</a>. To
|
|
download the example, clone the <code>grpc</code> repository by running the following
|
|
command:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git clone -b v1.20.0 https://github.com/grpc/grpc
|
|
$ cd grpc</code></pre></div>
|
|
<p>All the files for this tutorial are in the directory
|
|
<code>examples/csharp/RouteGuide</code>. Open the solution
|
|
<code>examples/csharp/RouteGuide/RouteGuide.sln</code> from Visual Studio (Windows or Mac) or Visual Studio Code.
|
|
For additional installation details, see the <a href="https://github.com/grpc/grpc/tree/
|
|
v1.20.0/src/csharp#how-to-use">How to use
|
|
instructions</a>.</p>
|
|
|
|
<h3 id="defining-the-service">Defining the service</h3>
|
|
|
|
<p>Our first step (as you’ll know from the <a href="/docs/">Overview</a>) is to
|
|
define the gRPC <em>service</em> and the method <em>request</em> and <em>response</em> types using
|
|
<a href="https://developers.google.com/protocol-buffers/docs/overview">protocol buffers</a>.
|
|
You can see the complete .proto file in
|
|
<a href="https://github.com/grpc/grpc/blob/
|
|
v1.20.0/examples/protos/route_guide.proto"><code>examples/protos/route_guide.proto</code></a>.</p>
|
|
|
|
<p>To define a service, you specify a named <code>service</code> in your .proto file:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#66d9ef">service</span> RouteGuide {<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#f92672">...</span><span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span>}</code></pre></div>
|
|
<p>Then you define <code>rpc</code> methods inside your service definition, specifying their
|
|
request and response types. gRPC lets you define four kinds of service method,
|
|
all of which are used in the <code>RouteGuide</code> service:</p>
|
|
|
|
<ul>
|
|
<li>A <em>simple RPC</em> where the client sends a request to the server using the client
|
|
object and waits for a response to come back, just like a normal function
|
|
call.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Obtains the feature at a given position.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> GetFeature(Point) <span style="color:#66d9ef">returns</span> (Feature) {}</code></pre></div>
|
|
<ul>
|
|
<li>A <em>server-side streaming RPC</em> where the client sends a request to the server
|
|
and gets a stream to read a sequence of messages back. The client reads from
|
|
the returned stream until there are no more messages. As you can see in our
|
|
example, you specify a server-side streaming method by placing the <code>stream</code>
|
|
keyword before the <em>response</em> type.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Obtains the Features available within the given Rectangle. Results are
|
|
</span><span style="color:#75715e">// streamed rather than returned at once (e.g. in a response message with a
|
|
</span><span style="color:#75715e">// repeated field), as the rectangle may cover a large area and contain a
|
|
</span><span style="color:#75715e">// huge number of features.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> ListFeatures(Rectangle) <span style="color:#66d9ef">returns</span> (stream Feature) {}</code></pre></div>
|
|
<ul>
|
|
<li>A <em>client-side streaming RPC</em> where the client writes a sequence of messages
|
|
and sends them to the server, again using a provided stream. Once the client
|
|
has finished writing the messages, it waits for the server to read them all
|
|
and return its response. You specify a client-side streaming method by placing
|
|
the <code>stream</code> keyword before the <em>request</em> type.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Accepts a stream of Points on a route being traversed, returning a
|
|
</span><span style="color:#75715e">// RouteSummary when traversal is completed.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> RecordRoute(stream Point) <span style="color:#66d9ef">returns</span> (RouteSummary) {}</code></pre></div>
|
|
<ul>
|
|
<li>A <em>bidirectional streaming RPC</em> where both sides send a sequence of messages
|
|
using a read-write stream. The two streams operate independently, so clients
|
|
and servers can read and write in whatever order they like: for example, the
|
|
server could wait to receive all the client messages before writing its
|
|
responses, or it could alternately read a message then write a message, or
|
|
some other combination of reads and writes. The order of messages in each
|
|
stream is preserved. You specify this type of method by placing the <code>stream</code>
|
|
keyword before both the request and the response.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Accepts a stream of RouteNotes sent while a route is being traversed,
|
|
</span><span style="color:#75715e">// while receiving other RouteNotes (e.g. from other users).
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> RouteChat(stream RouteNote) <span style="color:#66d9ef">returns</span> (stream RouteNote) {}</code></pre></div>
|
|
<p>Our .proto file also contains protocol buffer message type definitions for all
|
|
the request and response types used in our service methods - for example, here’s
|
|
the <code>Point</code> message type:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Points are represented as latitude-longitude pairs in the E7 representation
|
|
</span><span style="color:#75715e">// (degrees multiplied by 10**7 and rounded to the nearest integer).
|
|
</span><span style="color:#75715e">// Latitudes should be in the range +/- 90 degrees and longitude should be in
|
|
</span><span style="color:#75715e">// the range +/- 180 degrees (inclusive).
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">message</span> <span style="color:#a6e22e">Point</span> {<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#66d9ef">int32</span> latitude <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>;<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#66d9ef">int32</span> longitude <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>;<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span>}</code></pre></div>
|
|
<h3 id="generating-client-and-server-code">Generating client and server code</h3>
|
|
|
|
<p>Next we need to generate the gRPC client and server interfaces from our .proto
|
|
service definition. This can be done by invoking the protocol buffer compiler <code>protoc</code> with
|
|
a special gRPC C# plugin from the command line, but starting from version
|
|
1.17 the <code>Grpc.Tools</code> NuGet package integrates with MSBuild to provide <a href="https://github.com/grpc/grpc/blob/master/src/csharp/BUILD-INTEGRATION.md">automatic C# code generation</a>
|
|
from <code>.proto</code> files, which gives much better developer experience by running
|
|
the right commands for you as part of the build.</p>
|
|
|
|
<p>This example already has a dependency on <code>Grpc.Tools</code> NuGet package and the
|
|
<code>route_guide.proto</code> has already been added to the project, so the only thing
|
|
needed to generate the client and server code is to build the solution.
|
|
That can be done by running <code>dotnet build RouteGuide.sln</code> or building directly
|
|
in Visual Studio.</p>
|
|
|
|
<p>The build regenerates the following files
|
|
under the <code>RouteGuide/obj/Debug/TARGET_FRAMEWORK</code> directory:</p>
|
|
|
|
<ul>
|
|
<li><code>RouteGuide.cs</code> contains all the protocol buffer code to populate,
|
|
serialize, and retrieve our request and response message types</li>
|
|
<li><code>RouteGuideGrpc.cs</code> provides generated client and server classes,
|
|
including:
|
|
|
|
<ul>
|
|
<li>an abstract class <code>RouteGuide.RouteGuideBase</code> to inherit from when defining
|
|
RouteGuide service implementations</li>
|
|
<li>a class <code>RouteGuide.RouteGuideClient</code> that can be used to access remote
|
|
RouteGuide instances</li>
|
|
</ul></li>
|
|
</ul>
|
|
|
|
<p><a name="server"></a></p>
|
|
|
|
<h3 id="creating-the-server">Creating the server</h3>
|
|
|
|
<p>First let’s look at how we create a <code>RouteGuide</code> server. If you’re only
|
|
interested in creating gRPC clients, you can skip this section and go straight
|
|
to <a href="#client">Creating the client</a> (though you might find it interesting
|
|
anyway!).</p>
|
|
|
|
<p>There are two parts to making our <code>RouteGuide</code> service do its job:</p>
|
|
|
|
<ul>
|
|
<li>Implementing the service functionality by inheriting from the base class
|
|
generated from our service definition: doing the actual “work” of our service.</li>
|
|
<li>Running a gRPC server to listen for requests from clients and return the
|
|
service responses.</li>
|
|
</ul>
|
|
|
|
<p>You can find our example <code>RouteGuide</code> server in
|
|
<a href="https://github.com/grpc/grpc/blob/
|
|
v1.20.0/examples/csharp/RouteGuide/RouteGuideServer/RouteGuideImpl.cs">examples/csharp/RouteGuide/RouteGuideServer/RouteGuideImpl.cs</a>.
|
|
Let’s take a closer look at how it works.</p>
|
|
|
|
<h4 id="implementing-routeguide">Implementing RouteGuide</h4>
|
|
|
|
<p>As you can see, our server has a <code>RouteGuideImpl</code> class that inherits from the
|
|
generated <code>RouteGuide.RouteGuideBase</code>:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#75715e">// RouteGuideImpl provides an implementation of the RouteGuide service.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RouteGuideImpl</span> : RouteGuide.RouteGuideBase
|
|
</code></pre></div>
|
|
<h5 id="simple-rpc">Simple RPC</h5>
|
|
|
|
<p><code>RouteGuideImpl</code> implements all our service methods. Let’s look at the simplest
|
|
type first, <code>GetFeature</code>, which just gets a <code>Point</code> from the client and returns
|
|
the corresponding feature information from its database in a <code>Feature</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">override</span> Task<Feature> GetFeature(Point request, Grpc.Core.ServerCallContext context)
|
|
{
|
|
<span style="color:#66d9ef">return</span> Task.FromResult(CheckFeature(request));
|
|
}
|
|
</code></pre></div>
|
|
<p>The method is passed a context for the RPC (which is empty in the alpha
|
|
release), the client’s <code>Point</code> protocol buffer request, and returns a <code>Feature</code>
|
|
protocol buffer. In the method we create the <code>Feature</code> with the appropriate
|
|
information, and then return it. To allow asynchronous implementation, the
|
|
method returns <code>Task<Feature></code> rather than just <code>Feature</code>. You are free to
|
|
perform your computations synchronously and return the result once you’ve
|
|
finished, just as we do in the example.</p>
|
|
|
|
<h5 id="server-side-streaming-rpc">Server-side streaming RPC</h5>
|
|
|
|
<p>Now let’s look at something a bit more complicated - a streaming RPC.
|
|
<code>ListFeatures</code> is a server-side streaming RPC, so we need to send back multiple
|
|
<code>Feature</code> protocol buffers to our client.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#75715e">// in RouteGuideImpl
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">async</span> Task ListFeatures(Rectangle request,
|
|
Grpc.Core.IServerStreamWriter<Feature> responseStream,
|
|
Grpc.Core.ServerCallContext context)
|
|
{
|
|
<span style="color:#66d9ef">var</span> responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) );
|
|
<span style="color:#66d9ef">foreach</span> (<span style="color:#66d9ef">var</span> response <span style="color:#66d9ef">in</span> responses)
|
|
{
|
|
<span style="color:#66d9ef">await</span> responseStream.WriteAsync(response);
|
|
}
|
|
}
|
|
</code></pre></div>
|
|
<p>As you can see, here the request object is a <code>Rectangle</code> in which our client
|
|
wants to find <code>Feature</code>s, but instead of returning a simple response we need to
|
|
write responses to an asynchronous stream <code>IServerStreamWriter</code> using async
|
|
method <code>WriteAsync</code>.</p>
|
|
|
|
<h5 id="client-side-streaming-rpc">Client-side streaming RPC</h5>
|
|
|
|
<p>Similarly, the client-side streaming method <code>RecordRoute</code> uses an
|
|
<a href="https://github.com/Reactive-Extensions/Rx.NET/blob/master/Ix.NET/Source/System.Interactive.Async/IAsyncEnumerator.cs">IAsyncEnumerator</a>,
|
|
to read the stream of requests using the async method <code>MoveNext</code> and the
|
|
<code>Current</code> property.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">async</span> Task<RouteSummary> RecordRoute(Grpc.Core.IAsyncStreamReader<Point> requestStream,
|
|
Grpc.Core.ServerCallContext context)
|
|
{
|
|
<span style="color:#66d9ef">int</span> pointCount = <span style="color:#ae81ff">0</span>;
|
|
<span style="color:#66d9ef">int</span> featureCount = <span style="color:#ae81ff">0</span>;
|
|
<span style="color:#66d9ef">int</span> distance = <span style="color:#ae81ff">0</span>;
|
|
Point previous = <span style="color:#66d9ef">null</span>;
|
|
<span style="color:#66d9ef">var</span> stopwatch = <span style="color:#66d9ef">new</span> Stopwatch();
|
|
stopwatch.Start();
|
|
|
|
<span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">await</span> requestStream.MoveNext())
|
|
{
|
|
<span style="color:#66d9ef">var</span> point = requestStream.Current;
|
|
pointCount++;
|
|
<span style="color:#66d9ef">if</span> (CheckFeature(point).Exists())
|
|
{
|
|
featureCount++;
|
|
}
|
|
<span style="color:#66d9ef">if</span> (previous != <span style="color:#66d9ef">null</span>)
|
|
{
|
|
distance += (<span style="color:#66d9ef">int</span>) previous.GetDistance(point);
|
|
}
|
|
previous = point;
|
|
}
|
|
|
|
stopwatch.Stop();
|
|
|
|
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> RouteSummary
|
|
{
|
|
PointCount = pointCount,
|
|
FeatureCount = featureCount,
|
|
Distance = distance,
|
|
ElapsedTime = (<span style="color:#66d9ef">int</span>)(stopwatch.ElapsedMilliseconds / <span style="color:#ae81ff">1000</span>)
|
|
};
|
|
}
|
|
</code></pre></div>
|
|
<h5 id="bidirectional-streaming-rpc">Bidirectional streaming RPC</h5>
|
|
|
|
<p>Finally, let’s look at our bidirectional streaming RPC <code>RouteChat</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">override</span> <span style="color:#66d9ef">async</span> Task RouteChat(Grpc.Core.IAsyncStreamReader<RouteNote> requestStream,
|
|
Grpc.Core.IServerStreamWriter<RouteNote> responseStream,
|
|
Grpc.Core.ServerCallContext context)
|
|
{
|
|
<span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">await</span> requestStream.MoveNext())
|
|
{
|
|
<span style="color:#66d9ef">var</span> note = requestStream.Current;
|
|
List<RouteNote> prevNotes = AddNoteForLocation(note.Location, note);
|
|
<span style="color:#66d9ef">foreach</span> (<span style="color:#66d9ef">var</span> prevNote <span style="color:#66d9ef">in</span> prevNotes)
|
|
{
|
|
<span style="color:#66d9ef">await</span> responseStream.WriteAsync(prevNote);
|
|
}
|
|
}
|
|
}
|
|
</code></pre></div>
|
|
<p>Here the method receives both <code>requestStream</code> and <code>responseStream</code> arguments.
|
|
Reading the requests is done the same way as in the client-side streaming method
|
|
<code>RecordRoute</code>. Writing the responses is done the same way as in the server-side
|
|
streaming method <code>ListFeatures</code>.</p>
|
|
|
|
<h4 id="starting-the-server">Starting the server</h4>
|
|
|
|
<p>Once we’ve implemented all our methods, we also need to start up a gRPC server
|
|
so that clients can actually use our service. The following snippet shows how we
|
|
do this for our <code>RouteGuide</code> service:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">var</span> features = RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile);
|
|
|
|
Server server = <span style="color:#66d9ef">new</span> Server
|
|
{
|
|
Services = { RouteGuide.BindService(<span style="color:#66d9ef">new</span> RouteGuideImpl(features)) },
|
|
Ports = { <span style="color:#66d9ef">new</span> ServerPort(<span style="color:#e6db74">"localhost"</span>, Port, ServerCredentials.Insecure) }
|
|
};
|
|
server.Start();
|
|
|
|
Console.WriteLine(<span style="color:#e6db74">"RouteGuide server listening on port "</span> + port);
|
|
Console.WriteLine(<span style="color:#e6db74">"Press any key to stop the server..."</span>);
|
|
Console.ReadKey();
|
|
|
|
server.ShutdownAsync().Wait();
|
|
</code></pre></div>
|
|
<p>As you can see, we build and start our server using <code>Grpc.Core.Server</code> class. To
|
|
do this, we:</p>
|
|
|
|
<ol>
|
|
<li>Create an instance of <code>Grpc.Core.Server</code>.</li>
|
|
<li>Create an instance of our service implementation class <code>RouteGuideImpl</code>.</li>
|
|
<li>Register our service implementation by adding its service definition to the
|
|
<code>Services</code> collection (We obtain the service definition from the generated
|
|
<code>RouteGuide.BindService</code> method).</li>
|
|
<li>Specify the address and port we want to use to listen for client requests.
|
|
This is done by adding <code>ServerPort</code> to the <code>Ports</code> collection.</li>
|
|
<li>Call <code>Start</code> on the server instance to start an RPC server for our service.</li>
|
|
</ol>
|
|
|
|
<p><a name="client"></a></p>
|
|
|
|
<h3 id="creating-the-client">Creating the client</h3>
|
|
|
|
<p>In this section, we’ll look at creating a C# client for our <code>RouteGuide</code>
|
|
service. You can see our complete example client code in
|
|
<a href="https://github.com/grpc/grpc/blob/
|
|
v1.20.0/examples/csharp/RouteGuide/RouteGuideClient/Program.cs">examples/csharp/RouteGuide/RouteGuideClient/Program.cs</a>.</p>
|
|
|
|
<h4 id="creating-a-client-object">Creating a client object</h4>
|
|
|
|
<p>To call service methods, we first need to create a client object (also referred
|
|
to as <em>stub</em> for other gRPC languages).</p>
|
|
|
|
<p>First, we need to create a gRPC client channel that will connect to gRPC server.
|
|
Then, we create an instance of the <code>RouteGuite.RouteGuideClient</code> class generated
|
|
from our .proto, passing the channel as an argument.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp">Channel channel = <span style="color:#66d9ef">new</span> Channel(<span style="color:#e6db74">"127.0.0.1:50052"</span>, ChannelCredentials.Insecure);
|
|
<span style="color:#66d9ef">var</span> client = <span style="color:#66d9ef">new</span> RouteGuide.RouteGuideClient(channel);
|
|
|
|
<span style="color:#75715e">// YOUR CODE GOES HERE
|
|
</span><span style="color:#75715e"></span>
|
|
channel.ShutdownAsync().Wait();
|
|
</code></pre></div>
|
|
<h4 id="calling-service-methods">Calling service methods</h4>
|
|
|
|
<p>Now let’s look at how we call our service methods. gRPC C# provides asynchronous
|
|
versions of each of the supported method types. For convenience, gRPC C# also
|
|
provides a synchronous method stub, but only for simple (single request/single
|
|
response) RPCs.</p>
|
|
|
|
<h5 id="simple-rpc-1">Simple RPC</h5>
|
|
|
|
<p>Calling the simple RPC <code>GetFeature</code> in a synchronous way is nearly as
|
|
straightforward as calling a local method.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp">Point request = <span style="color:#66d9ef">new</span> Point { Latitude = <span style="color:#ae81ff">409146138</span>, Longitude = -<span style="color:#ae81ff">746188906</span> };
|
|
Feature feature = client.GetFeature(request);
|
|
</code></pre></div>
|
|
<p>As you can see, we create and populate a request protocol buffer object (in our
|
|
case <code>Point</code>), and call the desired method on the client object, passing it the
|
|
request. If the RPC finishes with success, the response protocol buffer (in our
|
|
case <code>Feature</code>) is returned. Otherwise, an exception of type <code>RpcException</code> is
|
|
thrown, indicating the status code of the problem.</p>
|
|
|
|
<p>Alternatively, if you are in an async context, you can call an asynchronous
|
|
version of the method and use the <code>await</code> keyword to await the result:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp">Point request = <span style="color:#66d9ef">new</span> Point { Latitude = <span style="color:#ae81ff">409146138</span>, Longitude = -<span style="color:#ae81ff">746188906</span> };
|
|
Feature feature = <span style="color:#66d9ef">await</span> client.GetFeatureAsync(request);
|
|
</code></pre></div>
|
|
<h5 id="streaming-rpcs">Streaming RPCs</h5>
|
|
|
|
<p>Now let’s look at our streaming methods. If you’ve already read <a href="#server">Creating the
|
|
server</a> some of this may look very familiar - streaming RPCs are
|
|
implemented in a similar way on both sides. The difference with respect to
|
|
simple call is that the client methods return an instance of a call object. This
|
|
provides access to request/response streams and/or the asynchronous result,
|
|
depending on the streaming type you are using.</p>
|
|
|
|
<p>Here’s where we call the server-side streaming method <code>ListFeatures</code>, which has
|
|
the property <code>ReponseStream</code> of type <code>IAsyncEnumerator<Feature></code></p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">using</span> (<span style="color:#66d9ef">var</span> call = client.ListFeatures(request))
|
|
{
|
|
<span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">await</span> call.ResponseStream.MoveNext())
|
|
{
|
|
Feature feature = call.ResponseStream.Current;
|
|
Console.WriteLine(<span style="color:#e6db74">"Received "</span> + feature.ToString());
|
|
}
|
|
}
|
|
</code></pre></div>
|
|
<p>The client-side streaming method <code>RecordRoute</code> is similar, except we use the
|
|
property <code>RequestStream</code> to write the requests one by one using <code>WriteAsync</code>,
|
|
and eventually signal that no more requests will be sent using <code>CompleteAsync</code>.
|
|
The method result can be obtained through the property <code>ResponseAsync</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">using</span> (<span style="color:#66d9ef">var</span> call = client.RecordRoute())
|
|
{
|
|
<span style="color:#66d9ef">foreach</span> (<span style="color:#66d9ef">var</span> point <span style="color:#66d9ef">in</span> points)
|
|
{
|
|
<span style="color:#66d9ef">await</span> call.RequestStream.WriteAsync(point);
|
|
}
|
|
<span style="color:#66d9ef">await</span> call.RequestStream.CompleteAsync();
|
|
|
|
RouteSummary summary = <span style="color:#66d9ef">await</span> call.ResponseAsync;
|
|
}
|
|
</code></pre></div>
|
|
<p>Finally, let’s look at our bidirectional streaming RPC <code>RouteChat</code>. In this
|
|
case, we write the request to <code>RequestStream</code> and receive the responses from
|
|
<code>ResponseStream</code>. As you can see from the example, the streams are independent
|
|
of each other.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-csharp" data-lang="csharp"><span style="color:#66d9ef">using</span> (<span style="color:#66d9ef">var</span> call = client.RouteChat())
|
|
{
|
|
<span style="color:#66d9ef">var</span> responseReaderTask = Task.Run(<span style="color:#66d9ef">async</span> () =>
|
|
{
|
|
<span style="color:#66d9ef">while</span> (<span style="color:#66d9ef">await</span> call.ResponseStream.MoveNext())
|
|
{
|
|
<span style="color:#66d9ef">var</span> note = call.ResponseStream.Current;
|
|
Console.WriteLine(<span style="color:#e6db74">"Received "</span> + note);
|
|
}
|
|
});
|
|
|
|
<span style="color:#66d9ef">foreach</span> (RouteNote request <span style="color:#66d9ef">in</span> requests)
|
|
{
|
|
<span style="color:#66d9ef">await</span> call.RequestStream.WriteAsync(request);
|
|
}
|
|
<span style="color:#66d9ef">await</span> call.RequestStream.CompleteAsync();
|
|
<span style="color:#66d9ef">await</span> responseReaderTask;
|
|
}
|
|
</code></pre></div>
|
|
<h3 id="try-it-out">Try it out!</h3>
|
|
|
|
<h4 id="build-the-client-and-server">Build the client and server:</h4>
|
|
|
|
<h5 id="using-visual-studio-or-visual-studio-for-mac">Using Visual Studio (or Visual Studio For Mac)</h5>
|
|
|
|
<ul>
|
|
<li>Open the solution <code>examples/csharp/RouteGuide/RouteGuide.sln</code> and select <strong>Build</strong>.</li>
|
|
</ul>
|
|
|
|
<h5 id="using-dotnet-command-line-tool">Using “dotnet” command line tool</h5>
|
|
|
|
<ul>
|
|
<li>Run <code>dotnet build RouteGuide.sln</code> from the <code>examples/csharp/RouteGuide</code> directory.
|
|
See the <a href="../../quickstart/csharp.html">quickstart</a> for additional instructions on building
|
|
the gRPC example with the <code>dotnet</code> command line tool.</li>
|
|
</ul>
|
|
|
|
<p>Run the server, which will listen on port 50052:</p>
|
|
|
|
<pre><code>> cd RouteGuideServer/bin/Debug/netcoreapp2.1
|
|
> dotnet exec RouteGuideServer.dll
|
|
</code></pre>
|
|
|
|
<p>Run the client (in a different terminal):</p>
|
|
|
|
<pre><code>> cd RouteGuideClient/bin/Debug/netcoreapp2.1
|
|
> dotnet exec RouteGuideClient.dll
|
|
</code></pre>
|
|
|
|
<p>You can also run the server and client directly from Visual Studio.</p>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
|
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
|
|
|
|
</body>
|
|
</html>
|