在C#中简单使用gRPC
阅读原文时间:2023年07月13日阅读:1

一、引言

  本文采用gRPC官方提供的一个教程例子,通过这个例子可以学习到在.proto文件中定义服务。使用protocol buffer编译器生成服务器和客户端代码。使用C#gRPC API为您的服务编写简单的客户端和服务器。具体可参看:https://grpc.io/docs/tutorials/basic/csharp/

二、具体步骤

  • 定义.proto文件

// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";
option objc_class_prefix = "RTG";

package routeguide;

// Interface exported by the server.
service RouteGuide {
// A simple RPC.
//
// Obtains the feature at a given position.
//
// A feature with an empty name is returned if there's no feature at the given
// position.
rpc GetFeature(Point) returns (Feature) {}

// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}

// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}

// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = ;
int32 longitude = ;
}

// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
message Rectangle {
// One corner of the rectangle.
Point lo = ;

// The other corner of the rectangle.
Point hi = ;
}

// A feature names something at a given point.
//
// If a feature could not be named, the name is empty.
message Feature {
// The name of the feature.
string name = ;

// The point where the feature is detected.
Point location = ;
}

// A RouteNote is a message sent while at a given point.
message RouteNote {
// The location from which the message is sent.
Point location = ;

// The message to be sent.
string message = ;
}

// A RouteSummary is received in response to a RecordRoute rpc.
//
// It contains the number of individual points received, the number of
// detected features, and the total distance covered as the cumulative sum of
// the distance between each point.
message RouteSummary {
// The number of points received.
int32 point_count = ;

// The number of known features passed while traversing the route.
int32 feature_count = ;

// The distance covered in metres.
int32 distance = ;

// The duration of the traversal in seconds.
int32 elapsed_time = ;
}

  • 需要引入Nuget包

Install-Package Google.Protobuf
Install-Package Grpc
Install-Package Grpc.Tools

  • 生成客户端和服务器代码,在Grpc.Tools 1.17版本之后 Grpc.ToolsNuGet包与MSBuild集成从.proto文件自动生成C#代码。但是在1.17以前的版本需要使用protoc.exe和grpc_csharp_plugin.exe 来生成代码,如下命令:

protoc -I . --csharp_out . --grpc_out . --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe Helloworld.proto

  在Grpc.Tools 1.17版本之后只需要使用donet build RouteGuid.sln或者直接在Visual Studio构建项目来完成,构建重新生成目录下的以下文件RouteGuide/obj/Debug/TARGET_FRAMEWORK:RouteGuide.cs和RouteGuideGrpc.cs

// // Generated by the protocol buffer compiler. DO NOT EDIT! // source: route_guide.proto //
#pragma warning disable 1591, 0612, 3021
#region Designer generated code

using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Routeguide {

///

Holder for reflection information generated from route_guide.proto
public static partial class RouteGuideReflection {

#region Descriptor  
/// <summary>File descriptor for route\_guide.proto</summary>  
public static pbr::FileDescriptor Descriptor {  
  get { return descriptor; }  
}  
private static pbr::FileDescriptor descriptor;

static RouteGuideReflection() {  
  byte\[\] descriptorData = global::System.Convert.FromBase64String(  
      string.Concat(  
        "ChFyb3V0ZV9ndWlkZS5wcm90bxIKcm91dGVndWlkZSIsCgVQb2ludBIQCghs",  
        "YXRpdHVkZRgBIAEoBRIRCglsb25naXR1ZGUYAiABKAUiSQoJUmVjdGFuZ2xl",  
        "Eh0KAmxvGAEgASgLMhEucm91dGVndWlkZS5Qb2ludBIdCgJoaRgCIAEoCzIR",  
        "LnJvdXRlZ3VpZGUuUG9pbnQiPAoHRmVhdHVyZRIMCgRuYW1lGAEgASgJEiMK",  
        "CGxvY2F0aW9uGAIgASgLMhEucm91dGVndWlkZS5Qb2ludCJBCglSb3V0ZU5v",  
        "dGUSIwoIbG9jYXRpb24YASABKAsyES5yb3V0ZWd1aWRlLlBvaW50Eg8KB21l",  
        "c3NhZ2UYAiABKAkiYgoMUm91dGVTdW1tYXJ5EhMKC3BvaW50X2NvdW50GAEg",  
        "ASgFEhUKDWZlYXR1cmVfY291bnQYAiABKAUSEAoIZGlzdGFuY2UYAyABKAUS",  
        "FAoMZWxhcHNlZF90aW1lGAQgASgFMoUCCgpSb3V0ZUd1aWRlEjYKCkdldEZl",  
        "YXR1cmUSES5yb3V0ZWd1aWRlLlBvaW50GhMucm91dGVndWlkZS5GZWF0dXJl",  
        "IgASPgoMTGlzdEZlYXR1cmVzEhUucm91dGVndWlkZS5SZWN0YW5nbGUaEy5y",  
        "b3V0ZWd1aWRlLkZlYXR1cmUiADABEj4KC1JlY29yZFJvdXRlEhEucm91dGVn",  
        "dWlkZS5Qb2ludBoYLnJvdXRlZ3VpZGUuUm91dGVTdW1tYXJ5IgAoARI/CglS",  
        "b3V0ZUNoYXQSFS5yb3V0ZWd1aWRlLlJvdXRlTm90ZRoVLnJvdXRlZ3VpZGUu",  
        "Um91dGVOb3RlIgAoATABQjYKG2lvLmdycGMuZXhhbXBsZXMucm91dGVndWlk",  
        "ZUIPUm91dGVHdWlkZVByb3RvUAGiAgNSVEdiBnByb3RvMw=="));  
  descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,  
      new pbr::FileDescriptor\[\] { },  
      new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo\[\] {  
        new pbr::GeneratedClrTypeInfo(typeof(global::Routeguide.Point), global::Routeguide.Point.Parser, new\[\]{ "Latitude", "Longitude" }, null, null, null),  
        new pbr::GeneratedClrTypeInfo(typeof(global::Routeguide.Rectangle), global::Routeguide.Rectangle.Parser, new\[\]{ "Lo", "Hi" }, null, null, null),  
        new pbr::GeneratedClrTypeInfo(typeof(global::Routeguide.Feature), global::Routeguide.Feature.Parser, new\[\]{ "Name", "Location" }, null, null, null),  
        new pbr::GeneratedClrTypeInfo(typeof(global::Routeguide.RouteNote), global::Routeguide.RouteNote.Parser, new\[\]{ "Location", "Message" }, null, null, null),  
        new pbr::GeneratedClrTypeInfo(typeof(global::Routeguide.RouteSummary), global::Routeguide.RouteSummary.Parser, new\[\]{ "PointCount", "FeatureCount", "Distance", "ElapsedTime" }, null, null, null)  
      }));  
}  
#endregion

}
#region Messages
///

/// Points are represented as latitude-longitude pairs in the E7 representation /// (degrees multiplied by 10**7 and rounded to the nearest integer). /// Latitudes should be in the range +/- 90 degrees and longitude should be in /// the range +/- 180 degrees (inclusive). ///
public sealed partial class Point : pb::IMessage {
private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Point());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser Parser { get { return _parser; } }

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public static pbr::MessageDescriptor Descriptor {  
  get { return global::Routeguide.RouteGuideReflection.Descriptor.MessageTypes\[\]; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
pbr::MessageDescriptor pb::IMessage.Descriptor {  
  get { return Descriptor; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Point() {  
  OnConstruction();  
}

partial void OnConstruction();

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Point(Point other) : this() {  
  latitude\_ = other.latitude\_;  
  longitude\_ = other.longitude\_;  
  \_unknownFields = pb::UnknownFieldSet.Clone(other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Point Clone() {  
  return new Point(this);  
}

/// <summary>Field number for the "latitude" field.</summary>  
public const int LatitudeFieldNumber = ;  
private int latitude\_;  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int Latitude {  
  get { return latitude\_; }  
  set {  
    latitude\_ = value;  
  }  
}

/// <summary>Field number for the "longitude" field.</summary>  
public const int LongitudeFieldNumber = ;  
private int longitude\_;  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int Longitude {  
  get { return longitude\_; }  
  set {  
    longitude\_ = value;  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override bool Equals(object other) {  
  return Equals(other as Point);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public bool Equals(Point other) {  
  if (ReferenceEquals(other, null)) {  
    return false;  
  }  
  if (ReferenceEquals(other, this)) {  
    return true;  
  }  
  if (Latitude != other.Latitude) return false;  
  if (Longitude != other.Longitude) return false;  
  return Equals(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override int GetHashCode() {  
  int hash = ;  
  if (Latitude != ) hash ^= Latitude.GetHashCode();  
  if (Longitude != ) hash ^= Longitude.GetHashCode();  
  if (\_unknownFields != null) {  
    hash ^= \_unknownFields.GetHashCode();  
  }  
  return hash;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override string ToString() {  
  return pb::JsonFormatter.ToDiagnosticString(this);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void WriteTo(pb::CodedOutputStream output) {  
  if (Latitude != ) {  
    output.WriteRawTag();  
    output.WriteInt32(Latitude);  
  }  
  if (Longitude != ) {  
    output.WriteRawTag();  
    output.WriteInt32(Longitude);  
  }  
  if (\_unknownFields != null) {  
    \_unknownFields.WriteTo(output);  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int CalculateSize() {  
  int size = ;  
  if (Latitude != ) {  
    size +=  + pb::CodedOutputStream.ComputeInt32Size(Latitude);  
  }  
  if (Longitude != ) {  
    size +=  + pb::CodedOutputStream.ComputeInt32Size(Longitude);  
  }  
  if (\_unknownFields != null) {  
    size += \_unknownFields.CalculateSize();  
  }  
  return size;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(Point other) {  
  if (other == null) {  
    return;  
  }  
  if (other.Latitude != ) {  
    Latitude = other.Latitude;  
  }  
  if (other.Longitude != ) {  
    Longitude = other.Longitude;  
  }  
  \_unknownFields = pb::UnknownFieldSet.MergeFrom(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(pb::CodedInputStream input) {  
  uint tag;  
  while ((tag = input.ReadTag()) != ) {  
    switch(tag) {  
      default:  
        \_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(\_unknownFields, input);  
        break;  
      case : {  
        Latitude = input.ReadInt32();  
        break;  
      }  
      case : {  
        Longitude = input.ReadInt32();  
        break;  
      }  
    }  
  }  
}

}

///

/// A latitude-longitude rectangle, represented as two diagonally opposite /// points "lo" and "hi". ///
public sealed partial class Rectangle : pb::IMessage {
private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Rectangle());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser Parser { get { return _parser; } }

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public static pbr::MessageDescriptor Descriptor {  
  get { return global::Routeguide.RouteGuideReflection.Descriptor.MessageTypes\[\]; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
pbr::MessageDescriptor pb::IMessage.Descriptor {  
  get { return Descriptor; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Rectangle() {  
  OnConstruction();  
}

partial void OnConstruction();

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Rectangle(Rectangle other) : this() {  
  lo\_ = other.lo\_ != null ? other.lo\_.Clone() : null;  
  hi\_ = other.hi\_ != null ? other.hi\_.Clone() : null;  
  \_unknownFields = pb::UnknownFieldSet.Clone(other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Rectangle Clone() {  
  return new Rectangle(this);  
}

/// <summary>Field number for the "lo" field.</summary>  
public const int LoFieldNumber = ;  
private global::Routeguide.Point lo\_;  
/// <summary>  
/// One corner of the rectangle.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public global::Routeguide.Point Lo {  
  get { return lo\_; }  
  set {  
    lo\_ = value;  
  }  
}

/// <summary>Field number for the "hi" field.</summary>  
public const int HiFieldNumber = ;  
private global::Routeguide.Point hi\_;  
/// <summary>  
/// The other corner of the rectangle.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public global::Routeguide.Point Hi {  
  get { return hi\_; }  
  set {  
    hi\_ = value;  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override bool Equals(object other) {  
  return Equals(other as Rectangle);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public bool Equals(Rectangle other) {  
  if (ReferenceEquals(other, null)) {  
    return false;  
  }  
  if (ReferenceEquals(other, this)) {  
    return true;  
  }  
  if (!object.Equals(Lo, other.Lo)) return false;  
  if (!object.Equals(Hi, other.Hi)) return false;  
  return Equals(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override int GetHashCode() {  
  int hash = ;  
  if (lo\_ != null) hash ^= Lo.GetHashCode();  
  if (hi\_ != null) hash ^= Hi.GetHashCode();  
  if (\_unknownFields != null) {  
    hash ^= \_unknownFields.GetHashCode();  
  }  
  return hash;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override string ToString() {  
  return pb::JsonFormatter.ToDiagnosticString(this);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void WriteTo(pb::CodedOutputStream output) {  
  if (lo\_ != null) {  
    output.WriteRawTag();  
    output.WriteMessage(Lo);  
  }  
  if (hi\_ != null) {  
    output.WriteRawTag();  
    output.WriteMessage(Hi);  
  }  
  if (\_unknownFields != null) {  
    \_unknownFields.WriteTo(output);  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int CalculateSize() {  
  int size = ;  
  if (lo\_ != null) {  
    size +=  + pb::CodedOutputStream.ComputeMessageSize(Lo);  
  }  
  if (hi\_ != null) {  
    size +=  + pb::CodedOutputStream.ComputeMessageSize(Hi);  
  }  
  if (\_unknownFields != null) {  
    size += \_unknownFields.CalculateSize();  
  }  
  return size;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(Rectangle other) {  
  if (other == null) {  
    return;  
  }  
  if (other.lo\_ != null) {  
    if (lo\_ == null) {  
      lo\_ = new global::Routeguide.Point();  
    }  
    Lo.MergeFrom(other.Lo);  
  }  
  if (other.hi\_ != null) {  
    if (hi\_ == null) {  
      hi\_ = new global::Routeguide.Point();  
    }  
    Hi.MergeFrom(other.Hi);  
  }  
  \_unknownFields = pb::UnknownFieldSet.MergeFrom(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(pb::CodedInputStream input) {  
  uint tag;  
  while ((tag = input.ReadTag()) != ) {  
    switch(tag) {  
      default:  
        \_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(\_unknownFields, input);  
        break;  
      case : {  
        if (lo\_ == null) {  
          lo\_ = new global::Routeguide.Point();  
        }  
        input.ReadMessage(lo\_);  
        break;  
      }  
      case : {  
        if (hi\_ == null) {  
          hi\_ = new global::Routeguide.Point();  
        }  
        input.ReadMessage(hi\_);  
        break;  
      }  
    }  
  }  
}

}

///

/// A feature names something at a given point. /// /// If a feature could not be named, the name is empty. ///
public sealed partial class Feature : pb::IMessage {
private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Feature());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser Parser { get { return _parser; } }

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public static pbr::MessageDescriptor Descriptor {  
  get { return global::Routeguide.RouteGuideReflection.Descriptor.MessageTypes\[\]; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
pbr::MessageDescriptor pb::IMessage.Descriptor {  
  get { return Descriptor; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Feature() {  
  OnConstruction();  
}

partial void OnConstruction();

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Feature(Feature other) : this() {  
  name\_ = other.name\_;  
  location\_ = other.location\_ != null ? other.location\_.Clone() : null;  
  \_unknownFields = pb::UnknownFieldSet.Clone(other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public Feature Clone() {  
  return new Feature(this);  
}

/// <summary>Field number for the "name" field.</summary>  
public const int NameFieldNumber = ;  
private string name\_ = "";  
/// <summary>  
/// The name of the feature.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public string Name {  
  get { return name\_; }  
  set {  
    name\_ = pb::ProtoPreconditions.CheckNotNull(value, "value");  
  }  
}

/// <summary>Field number for the "location" field.</summary>  
public const int LocationFieldNumber = ;  
private global::Routeguide.Point location\_;  
/// <summary>  
/// The point where the feature is detected.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public global::Routeguide.Point Location {  
  get { return location\_; }  
  set {  
    location\_ = value;  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override bool Equals(object other) {  
  return Equals(other as Feature);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public bool Equals(Feature other) {  
  if (ReferenceEquals(other, null)) {  
    return false;  
  }  
  if (ReferenceEquals(other, this)) {  
    return true;  
  }  
  if (Name != other.Name) return false;  
  if (!object.Equals(Location, other.Location)) return false;  
  return Equals(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override int GetHashCode() {  
  int hash = ;  
  if (Name.Length != ) hash ^= Name.GetHashCode();  
  if (location\_ != null) hash ^= Location.GetHashCode();  
  if (\_unknownFields != null) {  
    hash ^= \_unknownFields.GetHashCode();  
  }  
  return hash;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override string ToString() {  
  return pb::JsonFormatter.ToDiagnosticString(this);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void WriteTo(pb::CodedOutputStream output) {  
  if (Name.Length != ) {  
    output.WriteRawTag();  
    output.WriteString(Name);  
  }  
  if (location\_ != null) {  
    output.WriteRawTag();  
    output.WriteMessage(Location);  
  }  
  if (\_unknownFields != null) {  
    \_unknownFields.WriteTo(output);  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int CalculateSize() {  
  int size = ;  
  if (Name.Length != ) {  
    size +=  + pb::CodedOutputStream.ComputeStringSize(Name);  
  }  
  if (location\_ != null) {  
    size +=  + pb::CodedOutputStream.ComputeMessageSize(Location);  
  }  
  if (\_unknownFields != null) {  
    size += \_unknownFields.CalculateSize();  
  }  
  return size;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(Feature other) {  
  if (other == null) {  
    return;  
  }  
  if (other.Name.Length != ) {  
    Name = other.Name;  
  }  
  if (other.location\_ != null) {  
    if (location\_ == null) {  
      location\_ = new global::Routeguide.Point();  
    }  
    Location.MergeFrom(other.Location);  
  }  
  \_unknownFields = pb::UnknownFieldSet.MergeFrom(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(pb::CodedInputStream input) {  
  uint tag;  
  while ((tag = input.ReadTag()) != ) {  
    switch(tag) {  
      default:  
        \_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(\_unknownFields, input);  
        break;  
      case : {  
        Name = input.ReadString();  
        break;  
      }  
      case : {  
        if (location\_ == null) {  
          location\_ = new global::Routeguide.Point();  
        }  
        input.ReadMessage(location\_);  
        break;  
      }  
    }  
  }  
}

}

///

/// A RouteNote is a message sent while at a given point. ///
public sealed partial class RouteNote : pb::IMessage {
private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RouteNote());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser Parser { get { return _parser; } }

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public static pbr::MessageDescriptor Descriptor {  
  get { return global::Routeguide.RouteGuideReflection.Descriptor.MessageTypes\[\]; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
pbr::MessageDescriptor pb::IMessage.Descriptor {  
  get { return Descriptor; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public RouteNote() {  
  OnConstruction();  
}

partial void OnConstruction();

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public RouteNote(RouteNote other) : this() {  
  location\_ = other.location\_ != null ? other.location\_.Clone() : null;  
  message\_ = other.message\_;  
  \_unknownFields = pb::UnknownFieldSet.Clone(other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public RouteNote Clone() {  
  return new RouteNote(this);  
}

/// <summary>Field number for the "location" field.</summary>  
public const int LocationFieldNumber = ;  
private global::Routeguide.Point location\_;  
/// <summary>  
/// The location from which the message is sent.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public global::Routeguide.Point Location {  
  get { return location\_; }  
  set {  
    location\_ = value;  
  }  
}

/// <summary>Field number for the "message" field.</summary>  
public const int MessageFieldNumber = ;  
private string message\_ = "";  
/// <summary>  
/// The message to be sent.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public string Message {  
  get { return message\_; }  
  set {  
    message\_ = pb::ProtoPreconditions.CheckNotNull(value, "value");  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override bool Equals(object other) {  
  return Equals(other as RouteNote);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public bool Equals(RouteNote other) {  
  if (ReferenceEquals(other, null)) {  
    return false;  
  }  
  if (ReferenceEquals(other, this)) {  
    return true;  
  }  
  if (!object.Equals(Location, other.Location)) return false;  
  if (Message != other.Message) return false;  
  return Equals(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override int GetHashCode() {  
  int hash = ;  
  if (location\_ != null) hash ^= Location.GetHashCode();  
  if (Message.Length != ) hash ^= Message.GetHashCode();  
  if (\_unknownFields != null) {  
    hash ^= \_unknownFields.GetHashCode();  
  }  
  return hash;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override string ToString() {  
  return pb::JsonFormatter.ToDiagnosticString(this);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void WriteTo(pb::CodedOutputStream output) {  
  if (location\_ != null) {  
    output.WriteRawTag();  
    output.WriteMessage(Location);  
  }  
  if (Message.Length != ) {  
    output.WriteRawTag();  
    output.WriteString(Message);  
  }  
  if (\_unknownFields != null) {  
    \_unknownFields.WriteTo(output);  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int CalculateSize() {  
  int size = ;  
  if (location\_ != null) {  
    size +=  + pb::CodedOutputStream.ComputeMessageSize(Location);  
  }  
  if (Message.Length != ) {  
    size +=  + pb::CodedOutputStream.ComputeStringSize(Message);  
  }  
  if (\_unknownFields != null) {  
    size += \_unknownFields.CalculateSize();  
  }  
  return size;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(RouteNote other) {  
  if (other == null) {  
    return;  
  }  
  if (other.location\_ != null) {  
    if (location\_ == null) {  
      location\_ = new global::Routeguide.Point();  
    }  
    Location.MergeFrom(other.Location);  
  }  
  if (other.Message.Length != ) {  
    Message = other.Message;  
  }  
  \_unknownFields = pb::UnknownFieldSet.MergeFrom(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(pb::CodedInputStream input) {  
  uint tag;  
  while ((tag = input.ReadTag()) != ) {  
    switch(tag) {  
      default:  
        \_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(\_unknownFields, input);  
        break;  
      case : {  
        if (location\_ == null) {  
          location\_ = new global::Routeguide.Point();  
        }  
        input.ReadMessage(location\_);  
        break;  
      }  
      case : {  
        Message = input.ReadString();  
        break;  
      }  
    }  
  }  
}

}

///

/// A RouteSummary is received in response to a RecordRoute rpc. /// /// It contains the number of individual points received, the number of /// detected features, and the total distance covered as the cumulative sum of /// the distance between each point. ///
public sealed partial class RouteSummary : pb::IMessage {
private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RouteSummary());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser Parser { get { return _parser; } }

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public static pbr::MessageDescriptor Descriptor {  
  get { return global::Routeguide.RouteGuideReflection.Descriptor.MessageTypes\[\]; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
pbr::MessageDescriptor pb::IMessage.Descriptor {  
  get { return Descriptor; }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public RouteSummary() {  
  OnConstruction();  
}

partial void OnConstruction();

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public RouteSummary(RouteSummary other) : this() {  
  pointCount\_ = other.pointCount\_;  
  featureCount\_ = other.featureCount\_;  
  distance\_ = other.distance\_;  
  elapsedTime\_ = other.elapsedTime\_;  
  \_unknownFields = pb::UnknownFieldSet.Clone(other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public RouteSummary Clone() {  
  return new RouteSummary(this);  
}

/// <summary>Field number for the "point\_count" field.</summary>  
public const int PointCountFieldNumber = ;  
private int pointCount\_;  
/// <summary>  
/// The number of points received.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int PointCount {  
  get { return pointCount\_; }  
  set {  
    pointCount\_ = value;  
  }  
}

/// <summary>Field number for the "feature\_count" field.</summary>  
public const int FeatureCountFieldNumber = ;  
private int featureCount\_;  
/// <summary>  
/// The number of known features passed while traversing the route.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int FeatureCount {  
  get { return featureCount\_; }  
  set {  
    featureCount\_ = value;  
  }  
}

/// <summary>Field number for the "distance" field.</summary>  
public const int DistanceFieldNumber = ;  
private int distance\_;  
/// <summary>  
/// The distance covered in metres.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int Distance {  
  get { return distance\_; }  
  set {  
    distance\_ = value;  
  }  
}

/// <summary>Field number for the "elapsed\_time" field.</summary>  
public const int ElapsedTimeFieldNumber = ;  
private int elapsedTime\_;  
/// <summary>  
/// The duration of the traversal in seconds.  
/// </summary>  
\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int ElapsedTime {  
  get { return elapsedTime\_; }  
  set {  
    elapsedTime\_ = value;  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override bool Equals(object other) {  
  return Equals(other as RouteSummary);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public bool Equals(RouteSummary other) {  
  if (ReferenceEquals(other, null)) {  
    return false;  
  }  
  if (ReferenceEquals(other, this)) {  
    return true;  
  }  
  if (PointCount != other.PointCount) return false;  
  if (FeatureCount != other.FeatureCount) return false;  
  if (Distance != other.Distance) return false;  
  if (ElapsedTime != other.ElapsedTime) return false;  
  return Equals(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override int GetHashCode() {  
  int hash = ;  
  if (PointCount != ) hash ^= PointCount.GetHashCode();  
  if (FeatureCount != ) hash ^= FeatureCount.GetHashCode();  
  if (Distance != ) hash ^= Distance.GetHashCode();  
  if (ElapsedTime != ) hash ^= ElapsedTime.GetHashCode();  
  if (\_unknownFields != null) {  
    hash ^= \_unknownFields.GetHashCode();  
  }  
  return hash;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public override string ToString() {  
  return pb::JsonFormatter.ToDiagnosticString(this);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void WriteTo(pb::CodedOutputStream output) {  
  if (PointCount != ) {  
    output.WriteRawTag();  
    output.WriteInt32(PointCount);  
  }  
  if (FeatureCount != ) {  
    output.WriteRawTag();  
    output.WriteInt32(FeatureCount);  
  }  
  if (Distance != ) {  
    output.WriteRawTag();  
    output.WriteInt32(Distance);  
  }  
  if (ElapsedTime != ) {  
    output.WriteRawTag();  
    output.WriteInt32(ElapsedTime);  
  }  
  if (\_unknownFields != null) {  
    \_unknownFields.WriteTo(output);  
  }  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public int CalculateSize() {  
  int size = ;  
  if (PointCount != ) {  
    size +=  + pb::CodedOutputStream.ComputeInt32Size(PointCount);  
  }  
  if (FeatureCount != ) {  
    size +=  + pb::CodedOutputStream.ComputeInt32Size(FeatureCount);  
  }  
  if (Distance != ) {  
    size +=  + pb::CodedOutputStream.ComputeInt32Size(Distance);  
  }  
  if (ElapsedTime != ) {  
    size +=  + pb::CodedOutputStream.ComputeInt32Size(ElapsedTime);  
  }  
  if (\_unknownFields != null) {  
    size += \_unknownFields.CalculateSize();  
  }  
  return size;  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(RouteSummary other) {  
  if (other == null) {  
    return;  
  }  
  if (other.PointCount != ) {  
    PointCount = other.PointCount;  
  }  
  if (other.FeatureCount != ) {  
    FeatureCount = other.FeatureCount;  
  }  
  if (other.Distance != ) {  
    Distance = other.Distance;  
  }  
  if (other.ElapsedTime != ) {  
    ElapsedTime = other.ElapsedTime;  
  }  
  \_unknownFields = pb::UnknownFieldSet.MergeFrom(\_unknownFields, other.\_unknownFields);  
}

\[global::System.Diagnostics.DebuggerNonUserCodeAttribute\]  
public void MergeFrom(pb::CodedInputStream input) {  
  uint tag;  
  while ((tag = input.ReadTag()) != ) {  
    switch(tag) {  
      default:  
        \_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(\_unknownFields, input);  
        break;  
      case : {  
        PointCount = input.ReadInt32();  
        break;  
      }  
      case : {  
        FeatureCount = input.ReadInt32();  
        break;  
      }  
      case : {  
        Distance = input.ReadInt32();  
        break;  
      }  
      case : {  
        ElapsedTime = input.ReadInt32();  
        break;  
      }  
    }  
  }  
}

}

#endregion

}

#endregion Designer generated code

// // Generated by the protocol buffer compiler. DO NOT EDIT! // source: route_guide.proto //
// Original file comments:
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#pragma warning disable 0414, 1591
#region Designer generated code

using grpc = global::Grpc.Core;

namespace Routeguide {
///

/// Interface exported by the server. ///
public static partial class RouteGuide
{
static readonly string __ServiceName = "routeguide.RouteGuide";

static readonly grpc::Marshaller<global::Routeguide.Point> \_\_Marshaller\_routeguide\_Point = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.Point.Parser.ParseFrom);  
static readonly grpc::Marshaller<global::Routeguide.Feature> \_\_Marshaller\_routeguide\_Feature = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.Feature.Parser.ParseFrom);  
static readonly grpc::Marshaller<global::Routeguide.Rectangle> \_\_Marshaller\_routeguide\_Rectangle = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.Rectangle.Parser.ParseFrom);  
static readonly grpc::Marshaller<global::Routeguide.RouteSummary> \_\_Marshaller\_routeguide\_RouteSummary = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.RouteSummary.Parser.ParseFrom);  
static readonly grpc::Marshaller<global::Routeguide.RouteNote> \_\_Marshaller\_routeguide\_RouteNote = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Routeguide.RouteNote.Parser.ParseFrom);

static readonly grpc::Method<global::Routeguide.Point, global::Routeguide.Feature> \_\_Method\_GetFeature = new grpc::Method<global::Routeguide.Point, global::Routeguide.Feature>(  
    grpc::MethodType.Unary,  
    \_\_ServiceName,  
    "GetFeature",  
    \_\_Marshaller\_routeguide\_Point,  
    \_\_Marshaller\_routeguide\_Feature);

static readonly grpc::Method<global::Routeguide.Rectangle, global::Routeguide.Feature> \_\_Method\_ListFeatures = new grpc::Method<global::Routeguide.Rectangle, global::Routeguide.Feature>(  
    grpc::MethodType.ServerStreaming,  
    \_\_ServiceName,  
    "ListFeatures",  
    \_\_Marshaller\_routeguide\_Rectangle,  
    \_\_Marshaller\_routeguide\_Feature);

static readonly grpc::Method<global::Routeguide.Point, global::Routeguide.RouteSummary> \_\_Method\_RecordRoute = new grpc::Method<global::Routeguide.Point, global::Routeguide.RouteSummary>(  
    grpc::MethodType.ClientStreaming,  
    \_\_ServiceName,  
    "RecordRoute",  
    \_\_Marshaller\_routeguide\_Point,  
    \_\_Marshaller\_routeguide\_RouteSummary);

static readonly grpc::Method<global::Routeguide.RouteNote, global::Routeguide.RouteNote> \_\_Method\_RouteChat = new grpc::Method<global::Routeguide.RouteNote, global::Routeguide.RouteNote>(  
    grpc::MethodType.DuplexStreaming,  
    \_\_ServiceName,  
    "RouteChat",  
    \_\_Marshaller\_routeguide\_RouteNote,  
    \_\_Marshaller\_routeguide\_RouteNote);

/// <summary>Service descriptor</summary>  
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor  
{  
  get { return global::Routeguide.RouteGuideReflection.Descriptor.Services\[\]; }  
}

/// <summary>Base class for server-side implementations of RouteGuide</summary>  
public abstract partial class RouteGuideBase  
{  
  /// <summary>  
  /// A simple RPC.  
  ///  
  /// Obtains the feature at a given position.  
  ///  
  /// A feature with an empty name is returned if there's no feature at the given  
  /// position.  
  /// </summary>  
  /// <param name="request">The request received from the client.</param>  
  /// <param name="context">The context of the server-side call handler being invoked.</param>  
  /// <returns>The response to send back to the client (wrapped by a task).</returns>  
  public virtual global::System.Threading.Tasks.Task<global::Routeguide.Feature> GetFeature(global::Routeguide.Point request, grpc::ServerCallContext context)  
  {  
    throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));  
  }

  /// <summary>  
  /// A server-to-client streaming RPC.  
  ///  
  /// Obtains the Features available within the given Rectangle.  Results are  
  /// streamed rather than returned at once (e.g. in a response message with a  
  /// repeated field), as the rectangle may cover a large area and contain a  
  /// huge number of features.  
  /// </summary>  
  /// <param name="request">The request received from the client.</param>  
  /// <param name="responseStream">Used for sending responses back to the client.</param>  
  /// <param name="context">The context of the server-side call handler being invoked.</param>  
  /// <returns>A task indicating completion of the handler.</returns>  
  public virtual global::System.Threading.Tasks.Task ListFeatures(global::Routeguide.Rectangle request, grpc::IServerStreamWriter<global::Routeguide.Feature> responseStream, grpc::ServerCallContext context)  
  {  
    throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));  
  }

  /// <summary>  
  /// A client-to-server streaming RPC.  
  ///  
  /// Accepts a stream of Points on a route being traversed, returning a  
  /// RouteSummary when traversal is completed.  
  /// </summary>  
  /// <param name="requestStream">Used for reading requests from the client.</param>  
  /// <param name="context">The context of the server-side call handler being invoked.</param>  
  /// <returns>The response to send back to the client (wrapped by a task).</returns>  
  public virtual global::System.Threading.Tasks.Task<global::Routeguide.RouteSummary> RecordRoute(grpc::IAsyncStreamReader<global::Routeguide.Point> requestStream, grpc::ServerCallContext context)  
  {  
    throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));  
  }

  /// <summary>  
  /// A Bidirectional streaming RPC.  
  ///  
  /// Accepts a stream of RouteNotes sent while a route is being traversed,  
  /// while receiving other RouteNotes (e.g. from other users).  
  /// </summary>  
  /// <param name="requestStream">Used for reading requests from the client.</param>  
  /// <param name="responseStream">Used for sending responses back to the client.</param>  
  /// <param name="context">The context of the server-side call handler being invoked.</param>  
  /// <returns>A task indicating completion of the handler.</returns>  
  public virtual global::System.Threading.Tasks.Task RouteChat(grpc::IAsyncStreamReader<global::Routeguide.RouteNote> requestStream, grpc::IServerStreamWriter<global::Routeguide.RouteNote> responseStream, grpc::ServerCallContext context)  
  {  
    throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));  
  }

}

/// <summary>Client for RouteGuide</summary>  
public partial class RouteGuideClient : grpc::ClientBase<RouteGuideClient>  
{  
  /// <summary>Creates a new client for RouteGuide</summary>  
  /// <param name="channel">The channel to use to make remote calls.</param>  
  public RouteGuideClient(grpc::Channel channel) : base(channel)  
  {  
  }  
  /// <summary>Creates a new client for RouteGuide that uses a custom <c>CallInvoker</c>.</summary>  
  /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>  
  public RouteGuideClient(grpc::CallInvoker callInvoker) : base(callInvoker)  
  {  
  }  
  /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>  
  protected RouteGuideClient() : base()  
  {  
  }  
  /// <summary>Protected constructor to allow creation of configured clients.</summary>  
  /// <param name="configuration">The client configuration.</param>  
  protected RouteGuideClient(ClientBaseConfiguration configuration) : base(configuration)  
  {  
  }

  /// <summary>  
  /// A simple RPC.  
  ///  
  /// Obtains the feature at a given position.  
  ///  
  /// A feature with an empty name is returned if there's no feature at the given  
  /// position.  
  /// </summary>  
  /// <param name="request">The request to send to the server.</param>  
  /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>  
  /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>  
  /// <param name="cancellationToken">An optional token for canceling the call.</param>  
  /// <returns>The response received from the server.</returns>  
  public virtual global::Routeguide.Feature GetFeature(global::Routeguide.Point request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))  
  {  
    return GetFeature(request, new grpc::CallOptions(headers, deadline, cancellationToken));  
  }  
  /// <summary>  
  /// A simple RPC.  
  ///  
  /// Obtains the feature at a given position.  
  ///  
  /// A feature with an empty name is returned if there's no feature at the given  
  /// position.  
  /// </summary>  
  /// <param name="request">The request to send to the server.</param>  
  /// <param name="options">The options for the call.</param>  
  /// <returns>The response received from the server.</returns>  
  public virtual global::Routeguide.Feature GetFeature(global::Routeguide.Point request, grpc::CallOptions options)  
  {  
    return CallInvoker.BlockingUnaryCall(\_\_Method\_GetFeature, null, options, request);  
  }  
  /// <summary>  
  /// A simple RPC.  
  ///  
  /// Obtains the feature at a given position.  
  ///  
  /// A feature with an empty name is returned if there's no feature at the given  
  /// position.  
  /// </summary>  
  /// <param name="request">The request to send to the server.</param>  
  /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>  
  /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>  
  /// <param name="cancellationToken">An optional token for canceling the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncUnaryCall<global::Routeguide.Feature> GetFeatureAsync(global::Routeguide.Point request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))  
  {  
    return GetFeatureAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken));  
  }  
  /// <summary>  
  /// A simple RPC.  
  ///  
  /// Obtains the feature at a given position.  
  ///  
  /// A feature with an empty name is returned if there's no feature at the given  
  /// position.  
  /// </summary>  
  /// <param name="request">The request to send to the server.</param>  
  /// <param name="options">The options for the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncUnaryCall<global::Routeguide.Feature> GetFeatureAsync(global::Routeguide.Point request, grpc::CallOptions options)  
  {  
    return CallInvoker.AsyncUnaryCall(\_\_Method\_GetFeature, null, options, request);  
  }  
  /// <summary>  
  /// A server-to-client streaming RPC.  
  ///  
  /// Obtains the Features available within the given Rectangle.  Results are  
  /// streamed rather than returned at once (e.g. in a response message with a  
  /// repeated field), as the rectangle may cover a large area and contain a  
  /// huge number of features.  
  /// </summary>  
  /// <param name="request">The request to send to the server.</param>  
  /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>  
  /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>  
  /// <param name="cancellationToken">An optional token for canceling the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncServerStreamingCall<global::Routeguide.Feature> ListFeatures(global::Routeguide.Rectangle request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))  
  {  
    return ListFeatures(request, new grpc::CallOptions(headers, deadline, cancellationToken));  
  }  
  /// <summary>  
  /// A server-to-client streaming RPC.  
  ///  
  /// Obtains the Features available within the given Rectangle.  Results are  
  /// streamed rather than returned at once (e.g. in a response message with a  
  /// repeated field), as the rectangle may cover a large area and contain a  
  /// huge number of features.  
  /// </summary>  
  /// <param name="request">The request to send to the server.</param>  
  /// <param name="options">The options for the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncServerStreamingCall<global::Routeguide.Feature> ListFeatures(global::Routeguide.Rectangle request, grpc::CallOptions options)  
  {  
    return CallInvoker.AsyncServerStreamingCall(\_\_Method\_ListFeatures, null, options, request);  
  }  
  /// <summary>  
  /// A client-to-server streaming RPC.  
  ///  
  /// Accepts a stream of Points on a route being traversed, returning a  
  /// RouteSummary when traversal is completed.  
  /// </summary>  
  /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>  
  /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>  
  /// <param name="cancellationToken">An optional token for canceling the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncClientStreamingCall<global::Routeguide.Point, global::Routeguide.RouteSummary> RecordRoute(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))  
  {  
    return RecordRoute(new grpc::CallOptions(headers, deadline, cancellationToken));  
  }  
  /// <summary>  
  /// A client-to-server streaming RPC.  
  ///  
  /// Accepts a stream of Points on a route being traversed, returning a  
  /// RouteSummary when traversal is completed.  
  /// </summary>  
  /// <param name="options">The options for the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncClientStreamingCall<global::Routeguide.Point, global::Routeguide.RouteSummary> RecordRoute(grpc::CallOptions options)  
  {  
    return CallInvoker.AsyncClientStreamingCall(\_\_Method\_RecordRoute, null, options);  
  }  
  /// <summary>  
  /// A Bidirectional streaming RPC.  
  ///  
  /// Accepts a stream of RouteNotes sent while a route is being traversed,  
  /// while receiving other RouteNotes (e.g. from other users).  
  /// </summary>  
  /// <param name="headers">The initial metadata to send with the call. This parameter is optional.</param>  
  /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>  
  /// <param name="cancellationToken">An optional token for canceling the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncDuplexStreamingCall<global::Routeguide.RouteNote, global::Routeguide.RouteNote> RouteChat(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))  
  {  
    return RouteChat(new grpc::CallOptions(headers, deadline, cancellationToken));  
  }  
  /// <summary>  
  /// A Bidirectional streaming RPC.  
  ///  
  /// Accepts a stream of RouteNotes sent while a route is being traversed,  
  /// while receiving other RouteNotes (e.g. from other users).  
  /// </summary>  
  /// <param name="options">The options for the call.</param>  
  /// <returns>The call object.</returns>  
  public virtual grpc::AsyncDuplexStreamingCall<global::Routeguide.RouteNote, global::Routeguide.RouteNote> RouteChat(grpc::CallOptions options)  
  {  
    return CallInvoker.AsyncDuplexStreamingCall(\_\_Method\_RouteChat, null, options);  
  }  
  /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>  
  protected override RouteGuideClient NewInstance(ClientBaseConfiguration configuration)  
  {  
    return new RouteGuideClient(configuration);  
  }  
}

/// <summary>Creates service definition that can be registered with a server</summary>  
/// <param name="serviceImpl">An object implementing the server-side handling logic.</param>  
public static grpc::ServerServiceDefinition BindService(RouteGuideBase serviceImpl)  
{  
  return grpc::ServerServiceDefinition.CreateBuilder()  
      .AddMethod(\_\_Method\_GetFeature, serviceImpl.GetFeature)  
      .AddMethod(\_\_Method\_ListFeatures, serviceImpl.ListFeatures)  
      .AddMethod(\_\_Method\_RecordRoute, serviceImpl.RecordRoute)  
      .AddMethod(\_\_Method\_RouteChat, serviceImpl.RouteChat).Build();  
}

/// <summary>Register service method implementations with a service binder. Useful when customizing the service binding logic.  
/// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>  
/// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>  
/// <param name="serviceImpl">An object implementing the server-side handling logic.</param>  
public static void BindService(grpc::ServiceBinderBase serviceBinder, RouteGuideBase serviceImpl)  
{  
  serviceBinder.AddMethod(\_\_Method\_GetFeature, serviceImpl.GetFeature);  
  serviceBinder.AddMethod(\_\_Method\_ListFeatures, serviceImpl.ListFeatures);  
  serviceBinder.AddMethod(\_\_Method\_RecordRoute, serviceImpl.RecordRoute);  
  serviceBinder.AddMethod(\_\_Method\_RouteChat, serviceImpl.RouteChat);  
}

}
}
#endregion

  • 创建服务器
  1. 通过继承从我们的服务定义生成的基类来实现服务功能:执行我们服务的实际“工作”。
  2. 运行gRPC服务器以侦听来自客户端的请求并返回服务响应。

// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Grpc.Core;
using Grpc.Core.Utils;

namespace Routeguide
{
///

/// Example implementation of RouteGuide server. ///
public class RouteGuideImpl : RouteGuide.RouteGuideBase
{
readonly List features;
readonly object myLock = new object();
readonly Dictionary> routeNotes = new Dictionary>();

    public RouteGuideImpl(List<Feature> features)  
    {  
        this.features = features;  
    }

    /// <summary>  
    /// Gets the feature at the requested point. If no feature at that location  
    /// exists, an unnammed feature is returned at the provided location.  
    /// </summary>  
    public override Task<Feature> GetFeature(Point request, ServerCallContext context)  
    {  
        return Task.FromResult(CheckFeature(request));  
    }

    /// <summary>  
    /// Gets all features contained within the given bounding rectangle.  
    /// </summary>  
    public override async Task ListFeatures(Rectangle request, IServerStreamWriter<Feature> responseStream, ServerCallContext context)  
    {  
        var responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) );  
        foreach (var response in responses)  
        {  
            await responseStream.WriteAsync(response);  
        }  
    }

    /// <summary>  
    /// Gets a stream of points, and responds with statistics about the "trip": number of points,  
    /// number of known features visited, total distance traveled, and total time spent.  
    /// </summary>  
    public override async Task<RouteSummary> RecordRoute(IAsyncStreamReader<Point> requestStream, ServerCallContext context)  
    {  
        int pointCount = ;  
        int featureCount = ;  
        int distance = ;  
        Point previous = null;  
        var stopwatch = new Stopwatch();  
        stopwatch.Start();

        while (await requestStream.MoveNext())  
        {  
            var point = requestStream.Current;  
            pointCount++;  
            if (CheckFeature(point).Exists())  
            {  
                featureCount++;  
            }  
            if (previous != null)  
            {  
                distance += (int) previous.GetDistance(point);  
            }  
            previous = point;  
        }

        stopwatch.Stop();

        return new RouteSummary  
        {  
            PointCount = pointCount,  
            FeatureCount = featureCount,  
            Distance = distance,  
            ElapsedTime = (int)(stopwatch.ElapsedMilliseconds / )  
        };  
    }

    /// <summary>  
    /// Receives a stream of message/location pairs, and responds with a stream of all previous  
    /// messages at each of those locations.  
    /// </summary>  
    public override async Task RouteChat(IAsyncStreamReader<RouteNote> requestStream, IServerStreamWriter<RouteNote> responseStream, ServerCallContext context)  
    {  
        while (await requestStream.MoveNext())  
        {  
            var note = requestStream.Current;  
            List<RouteNote> prevNotes = AddNoteForLocation(note.Location, note);  
            foreach (var prevNote in prevNotes)  
            {  
                await responseStream.WriteAsync(prevNote);  
            }  
        }  
    }

    /// <summary>  
    /// Adds a note for location and returns a list of pre-existing notes for that location (not containing the newly added note).  
    /// </summary>  
    private List<RouteNote> AddNoteForLocation(Point location, RouteNote note)  
    {  
        lock (myLock)  
        {  
            List<RouteNote> notes;  
            if (!routeNotes.TryGetValue(location, out notes)) {  
                notes = new List<RouteNote>();  
                routeNotes.Add(location, notes);  
            }  
            var preexistingNotes = new List<RouteNote>(notes);  
            notes.Add(note);  
            return preexistingNotes;  
        }  
    }

    /// <summary>  
    /// Gets the feature at the given point.  
    /// </summary>  
    /// <param name="location">the location to check</param>  
    /// <returns>The feature object at the point Note that an empty name indicates no feature.</returns>  
    private Feature CheckFeature(Point location)  
    {  
        var result = features.FirstOrDefault((feature) => feature.Location.Equals(location));  
        if (result == null)  
        {  
            // No feature was found, return an unnamed feature.  
            return new Feature { Name = "", Location = location };  
        }  
        return result;  
    }  
}  

}

// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Routeguide
{
class Program
{
static void Main(string[] args)
{
const int Port = ;

        var features = RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile);

        Server server = new Server  
        {  
            Services = { RouteGuide.BindService(new RouteGuideImpl(features)) },  
            Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }  
        };  
        server.Start();

        Console.WriteLine("RouteGuide server listening on port " + Port);  
        Console.WriteLine("Press any key to stop the server...");  
        Console.ReadKey();

        server.ShutdownAsync().Wait();  
    }  
}  

}

  如您所见,我们使用Grpc.Core.Server类构建和启动我们的服务器。为此,我们:

  1. 创建一个实例Grpc.Core.Server
  2. 创建我们的服务实现类的实例RouteGuideImpl
  3. 通过将其服务定义添加到Services集合来注册我们的服务实现 (我们从生成的RouteGuide.BindService方法中获取服务定义 )。
  4. 指定我们要用于侦听客户端请求的地址和端口。这是通过添加ServerPortPorts集合来完成的。
  5. 调用Start服务器实例为我们的服务启动RPC服务器。
  •  创建客户端

// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Routeguide
{
class Program
{
///

/// Sample client code that makes gRPC calls to the server. ///
public class RouteGuideClient
{
readonly RouteGuide.RouteGuideClient client;

        public RouteGuideClient(RouteGuide.RouteGuideClient client)  
        {  
            this.client = client;  
        }

        /// <summary>  
        /// Blocking unary call example.  Calls GetFeature and prints the response.  
        /// </summary>  
        public void GetFeature(int lat, int lon)  
        {  
            try  
            {  
                Log("\*\*\* GetFeature: lat={0} lon={1}", lat, lon);

                Point request = new Point { Latitude = lat, Longitude = lon };

                Feature feature = client.GetFeature(request);  
                if (feature.Exists())  
                {  
                    Log("Found feature called \\"{0}\\" at {1}, {2}",  
                        feature.Name, feature.Location.GetLatitude(), feature.Location.GetLongitude());  
                }  
                else  
                {  
                    Log("Found no feature at {0}, {1}",  
                        feature.Location.GetLatitude(), feature.Location.GetLongitude());  
                }  
            }  
            catch (RpcException e)  
            {  
                Log("RPC failed " + e);  
                throw;  
            }  
        }

        /// <summary>  
        /// Server-streaming example. Calls listFeatures with a rectangle of interest. Prints each response feature as it arrives.  
        /// </summary>  
        public async Task ListFeatures(int lowLat, int lowLon, int hiLat, int hiLon)  
        {  
            try  
            {  
                Log("\*\*\* ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", lowLat, lowLon, hiLat,  
                    hiLon);

                Rectangle request = new Rectangle  
                {  
                    Lo = new Point { Latitude = lowLat, Longitude = lowLon },  
                    Hi = new Point { Latitude = hiLat, Longitude = hiLon }  
                };

                using (var call = client.ListFeatures(request))  
                {  
                    var responseStream = call.ResponseStream;  
                    StringBuilder responseLog = new StringBuilder("Result: ");

                    while (await responseStream.MoveNext())  
                    {  
                        Feature feature = responseStream.Current;  
                        responseLog.Append(feature.ToString());  
                    }  
                    Log(responseLog.ToString());  
                }  
            }  
            catch (RpcException e)  
            {  
                Log("RPC failed " + e);  
                throw;  
            }  
        }

        /// <summary>  
        /// Client-streaming example. Sends numPoints randomly chosen points from features  
        /// with a variable delay in between. Prints the statistics when they are sent from the server.  
        /// </summary>  
        public async Task RecordRoute(List<Feature> features, int numPoints)  
        {  
            try  
            {  
                Log("\*\*\* RecordRoute");  
                using (var call = client.RecordRoute())  
                {  
                    // Send numPoints points randomly selected from the features list.  
                    StringBuilder numMsg = new StringBuilder();  
                    Random rand = new Random();  
                    for (int i = ; i < numPoints; ++i)  
                    {  
                        int index = rand.Next(features.Count);  
                        Point point = features\[index\].Location;  
                        Log("Visiting point {0}, {1}", point.GetLatitude(), point.GetLongitude());

                        await call.RequestStream.WriteAsync(point);

                        // A bit of delay before sending the next one.  
                        await Task.Delay(rand.Next() + );  
                    }  
                    await call.RequestStream.CompleteAsync();

                    RouteSummary summary = await call.ResponseAsync;  
                    Log("Finished trip with {0} points. Passed {1} features. "  
                        + "Travelled {2} meters. It took {3} seconds.", summary.PointCount,  
                        summary.FeatureCount, summary.Distance, summary.ElapsedTime);

                    Log("Finished RecordRoute");  
                }  
            }  
            catch (RpcException e)  
            {  
                Log("RPC failed", e);  
                throw;  
            }  
        }

        /// <summary>  
        /// Bi-directional streaming example. Send some chat messages, and print any  
        /// chat messages that are sent from the server.  
        /// </summary>  
        public async Task RouteChat()  
        {  
            try  
            {  
                Log("\*\*\* RouteChat");  
                var requests = new List<RouteNote>  
                {  
                    NewNote("First message", , ),  
                    NewNote("Second message", , ),  
                    NewNote("Third message", , ),  
                    NewNote("Fourth message", , )  
                };

                using (var call = client.RouteChat())  
                {  
                    var responseReaderTask = Task.Run(async () =>  
                    {  
                        while (await call.ResponseStream.MoveNext())  
                        {  
                            var note = call.ResponseStream.Current;  
                            Log("Got message \\"{0}\\" at {1}, {2}", note.Message,  
                                note.Location.Latitude, note.Location.Longitude);  
                        }  
                    });

                    foreach (RouteNote request in requests)  
                    {  
                        Log("Sending message \\"{0}\\" at {1}, {2}", request.Message,  
                            request.Location.Latitude, request.Location.Longitude);

                        await call.RequestStream.WriteAsync(request);  
                    }  
                    await call.RequestStream.CompleteAsync();  
                    await responseReaderTask;

                    Log("Finished RouteChat");  
                }  
            }  
            catch (RpcException e)  
            {  
                Log("RPC failed", e);  
                throw;  
            }  
        }

        private void Log(string s, params object\[\] args)  
        {  
            Console.WriteLine(string.Format(s, args));  
        }

        private void Log(string s)  
        {  
            Console.WriteLine(s);  
        }

        private RouteNote NewNote(string message, int lat, int lon)  
        {  
            return new RouteNote  
            {  
                Message = message,  
                Location = new Point { Latitude = lat, Longitude = lon }  
            };  
        }  
    }

    static void Main(string\[\] args)  
    {  
        var channel = new Channel("127.0.0.1:50052", ChannelCredentials.Insecure);  
        var client = new RouteGuideClient(new RouteGuide.RouteGuideClient(channel));

        // Looking for a valid feature  
        client.GetFeature(, -);

        // Feature missing.  
        client.GetFeature(, );

        // Looking for features between 40, -75 and 42, -73.  
        client.ListFeatures(, -, , -).Wait();

        // Record a few randomly selected points from the features file.  
        client.RecordRoute(RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile), ).Wait();

        // Send and receive some notes.  
        client.RouteChat().Wait();

        channel.ShutdownAsync().Wait();  
        Console.WriteLine("Press any key to exit...");  
        Console.ReadKey();  
    }  
}  

}

  • 最后可以在Visual Studio中启动项目RouteGuideClient和RouteGuideServer

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器