├── CMakeLists.txt ├── README.md ├── grpc_async_client.cc ├── grpc_async_server.cc └── helloworld.proto /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Minimum CMake required 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | # Project 5 | project(grpcAsync CXX) 6 | 7 | # Protobuf 8 | set(protobuf_MODULE_COMPATIBLE TRUE) 9 | find_package(protobuf CONFIG REQUIRED) 10 | message(STATUS "Using protobuf ${protobuf_VERSION}") 11 | 12 | # gRPC 13 | find_package(gRPC CONFIG REQUIRED) 14 | message(STATUS "Using gRPC ${gRPC_VERSION}") 15 | 16 | # gRPC C++ plugin 17 | get_target_property(gRPC_CPP_PLUGIN_EXECUTABLE gRPC::grpc_cpp_plugin 18 | IMPORTED_LOCATION_RELEASE) 19 | 20 | # Proto file 21 | get_filename_component(hw_proto "helloworld.proto" ABSOLUTE) 22 | get_filename_component(hw_proto_path "${hw_proto}" PATH) 23 | 24 | # Generated sources 25 | protobuf_generate_cpp(hw_proto_srcs hw_proto_hdrs "${hw_proto}") 26 | set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.cc") 27 | set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.h") 28 | add_custom_command( 29 | OUTPUT "${hw_grpc_srcs}" "${hw_grpc_hdrs}" 30 | COMMAND protobuf::protoc 31 | ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" -I "${hw_proto_path}" 32 | --plugin=protoc-gen-grpc="${gRPC_CPP_PLUGIN_EXECUTABLE}" 33 | "${hw_proto}" 34 | DEPENDS "${hw_proto}") 35 | 36 | # Generated include directory 37 | include_directories("${CMAKE_CURRENT_BINARY_DIR}") 38 | 39 | foreach(_target 40 | grpc_async_client grpc_async_server) 41 | add_executable(${_target} "${_target}.cc" 42 | ${hw_proto_srcs} 43 | ${hw_grpc_srcs}) 44 | target_link_libraries(${_target} 45 | protobuf::libprotobuf 46 | gRPC::grpc++_unsecure) 47 | endforeach() 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grpc_async 2 | gRPC framework asynchronous operations 3 | 4 | For more detail read the article (in Russian): https://habr.com/ru/articles/340758/ 5 | -------------------------------------------------------------------------------- /grpc_async_client.cc: -------------------------------------------------------------------------------- 1 | 2 | #ifndef METEO_GRPC_CLIENT_H 3 | #define METEO_GRPC_CLIENT_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "assert.h" 13 | 14 | #include 15 | #include 16 | 17 | #include "helloworld.grpc.pb.h" 18 | 19 | 20 | using grpc::Channel; 21 | using grpc::ClientAsyncResponseReader; 22 | using grpc::ClientContext; 23 | using grpc::ClientAsyncReader; 24 | using grpc::ClientAsyncWriter; 25 | using grpc::ClientAsyncReaderWriter; 26 | using grpc::CompletionQueue; 27 | using grpc::Status; 28 | using helloworld::HelloRequest; 29 | using helloworld::HelloReply; 30 | using helloworld::Greeter; 31 | 32 | 33 | class AbstractAsyncClientCall 34 | { 35 | public: 36 | enum CallStatus { PROCESS, FINISH, DESTROY }; 37 | 38 | explicit AbstractAsyncClientCall():callStatus(PROCESS){} 39 | virtual ~AbstractAsyncClientCall(){} 40 | HelloReply reply; 41 | ClientContext context; 42 | Status status; 43 | CallStatus callStatus ; 44 | void printReply(const char* from) 45 | { 46 | if(!reply.message().empty()) 47 | std::cout << "[" << from << "]: reply message = " << reply.message() << std::endl; 48 | else 49 | std::cout << "[" << from << "]: reply message empty" << std::endl; 50 | 51 | } 52 | virtual void Proceed(bool = true) = 0; 53 | }; 54 | 55 | class AsyncClientCall: public AbstractAsyncClientCall 56 | { 57 | std::unique_ptr< ClientAsyncResponseReader > responder; 58 | public: 59 | AsyncClientCall(const HelloRequest& request, CompletionQueue& cq_, std::unique_ptr& stub_):AbstractAsyncClientCall() 60 | { 61 | std::cout << "[Proceed11]: new client 1-1" << std::endl; 62 | responder = stub_->AsyncSayHello(&context, request, &cq_); 63 | responder->Finish(&reply, &status, (void*)this); 64 | callStatus = PROCESS ; 65 | } 66 | virtual void Proceed(bool ok = true) override 67 | { 68 | if(callStatus == PROCESS) 69 | { 70 | // Verify that the request was completed successfully. Note that "ok" 71 | // corresponds solely to the request for updates introduced by Finish(). 72 | GPR_ASSERT(ok); 73 | if(status.ok()) 74 | printReply("Proceed11"); 75 | std::cout << "[Proceed11]: Good Bye" << std::endl; 76 | delete this; 77 | } 78 | } 79 | }; 80 | 81 | class AsyncClientCall1M : public AbstractAsyncClientCall 82 | { 83 | std::unique_ptr< ClientAsyncReader > responder; 84 | public: 85 | AsyncClientCall1M(const HelloRequest& request, CompletionQueue& cq_, std::unique_ptr& stub_):AbstractAsyncClientCall() 86 | { 87 | std::cout << "[Proceed1M]: new client 1-M" << std::endl; 88 | responder = stub_->AsyncGladToSeeMe(&context, request, &cq_, (void*)this); 89 | callStatus = PROCESS ; 90 | } 91 | virtual void Proceed(bool ok = true) override 92 | { 93 | if(callStatus == PROCESS) 94 | { 95 | if(!ok) 96 | { 97 | responder->Finish(&status, (void*)this); 98 | callStatus = FINISH; 99 | return ; 100 | } 101 | responder->Read(&reply, (void*)this); 102 | printReply("Proceed1M"); 103 | } 104 | else if(callStatus == FINISH) 105 | { 106 | std::cout << "[Proceed1M]: Good Bye" << std::endl; 107 | delete this; 108 | } 109 | return ; 110 | } 111 | }; 112 | 113 | class AsyncClientCallM1 : public AbstractAsyncClientCall 114 | { 115 | std::unique_ptr< ClientAsyncWriter > responder; 116 | unsigned mcounter; 117 | bool writing_mode_; 118 | public: 119 | AsyncClientCallM1(CompletionQueue& cq_, std::unique_ptr& stub_): 120 | AbstractAsyncClientCall(), mcounter(0),writing_mode_(true) 121 | { 122 | std::cout << "[ProceedM1]: new client M-1" << std::endl; 123 | responder = stub_->AsyncGladToSeeYou(&context, &reply, &cq_, (void*)this); 124 | callStatus = PROCESS ; 125 | } 126 | virtual void Proceed(bool ok = true) override 127 | { 128 | if(callStatus == PROCESS) 129 | { 130 | if(writing_mode_) 131 | { 132 | static std::vector greeting = {"Hello, server!", 133 | "Glad to see you!", 134 | "Haven't seen you for thousand years!", 135 | "I'm client now. Call me later."}; 136 | //std::cout << "[ProceedM1]: mcounter = " << mcounter << std::endl; 137 | if(mcounter < greeting.size()) 138 | { 139 | HelloRequest request; 140 | request.set_name(greeting.at(mcounter)); 141 | responder->Write(request, (void*)this); 142 | ++mcounter ; 143 | } 144 | else 145 | { 146 | responder->WritesDone((void*)this); 147 | std::cout << "[ProceedM1]: changing state to reading" << std::endl; 148 | writing_mode_ = false; 149 | return; 150 | } 151 | } 152 | else//reading mode 153 | { 154 | std::cout << "[ProceedM1]: trying finish" << std::endl; 155 | responder->Finish(&status, (void*)this); 156 | callStatus = FINISH ; 157 | } 158 | } 159 | else if(callStatus == FINISH) 160 | { 161 | assert(!reply.message().empty()); 162 | printReply("ProceedM1"); 163 | std::cout << "[ProceedM1]: Good Bye" << std::endl; 164 | delete this; 165 | } 166 | return ; 167 | } 168 | }; 169 | 170 | class AsyncClientCallMM : public AbstractAsyncClientCall 171 | { 172 | std::unique_ptr< ClientAsyncReaderWriter > responder; 173 | unsigned mcounter; 174 | bool writing_mode_; 175 | public: 176 | AsyncClientCallMM(CompletionQueue& cq_, std::unique_ptr& stub_): 177 | AbstractAsyncClientCall(), mcounter(0), writing_mode_(true) 178 | { 179 | std::cout << "[ProceedMM]: new client M-M" << std::endl; 180 | responder = stub_->AsyncBothGladToSee(&context, &cq_, (void*)this); 181 | callStatus = PROCESS ; 182 | } 183 | virtual void Proceed(bool ok = true) override 184 | { 185 | if(callStatus == PROCESS) 186 | { 187 | 188 | if(writing_mode_) 189 | { 190 | static std::vector greeting = {"Hello, server!", 191 | "Glad to see you!", 192 | "Haven't seen you for thousand years!", 193 | "I'm client now. Call me later."}; 194 | //std::cout << "[ProceedMM]: mcounter = " << mcounter << std::endl; 195 | if(mcounter < greeting.size()) 196 | { 197 | HelloRequest request; 198 | request.set_name(greeting.at(mcounter)); 199 | responder->Write(request, (void*)this); 200 | ++mcounter; 201 | } 202 | else 203 | { 204 | responder->WritesDone((void*)this); 205 | std::cout << "[ProceedMM]: changing state to reading" << std::endl; 206 | writing_mode_ = false; 207 | } 208 | return ; 209 | } 210 | else //reading mode 211 | { 212 | if(!ok) 213 | { 214 | std::cout << "[ProceedMM]: trying finish" << std::endl; 215 | callStatus = FINISH; 216 | responder->Finish(&status, (void*)this); 217 | return; 218 | } 219 | responder->Read(&reply, (void*)this); 220 | printReply("ProceedMM"); 221 | } 222 | return; 223 | } 224 | else if(callStatus == FINISH) 225 | { 226 | std::cout << "[ProceedMM]: Good Bye" << std::endl; 227 | delete this; 228 | } 229 | 230 | } 231 | }; 232 | 233 | 234 | 235 | 236 | class GreeterClient 237 | { 238 | public: 239 | explicit GreeterClient(std::shared_ptr channel) 240 | :stub_(Greeter::NewStub(channel)) 241 | {} 242 | 243 | // Assembles the client's payload and sends it to the server. 244 | void SayHello(const std::string& user) 245 | { 246 | HelloRequest request; 247 | request.set_name(user); 248 | new AsyncClientCall(request, cq_, stub_); 249 | } 250 | 251 | 252 | void GladToSeeMe(const std::string& user) 253 | { 254 | HelloRequest request; 255 | request.set_name(user); 256 | new AsyncClientCall1M(request, cq_, stub_); 257 | } 258 | 259 | 260 | void GladToSeeYou() 261 | { 262 | new AsyncClientCallM1(cq_, stub_); 263 | } 264 | 265 | 266 | void BothGladToSee() 267 | { 268 | new AsyncClientCallMM(cq_, stub_); 269 | } 270 | 271 | void AsyncCompleteRpc() 272 | { 273 | void* got_tag; 274 | bool ok = false; 275 | while(cq_.Next(&got_tag, &ok)) 276 | { 277 | AbstractAsyncClientCall* call = static_cast(got_tag); 278 | call->Proceed(ok); 279 | } 280 | std::cout << "Completion queue is shutting down." << std::endl; 281 | } 282 | 283 | private: 284 | // Out of the passed in Channel comes the stub, stored here, our view of the 285 | // server's exposed services. 286 | std::unique_ptr stub_; 287 | 288 | // The producer-consumer queue we use to communicate asynchronously with the 289 | // gRPC runtime. 290 | CompletionQueue cq_; 291 | }; 292 | 293 | 294 | int main(int argc, char* argv[]) 295 | { 296 | GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials())); 297 | std::thread thread_ = std::thread(&GreeterClient::AsyncCompleteRpc, &greeter); 298 | greeter.SayHello("world"); 299 | greeter.GladToSeeMe("client"); 300 | greeter.GladToSeeYou(); 301 | greeter.BothGladToSee(); 302 | thread_.join(); 303 | 304 | } 305 | 306 | 307 | #endif 308 | -------------------------------------------------------------------------------- /grpc_async_server.cc: -------------------------------------------------------------------------------- 1 | 2 | #ifndef METEO_GRPC_SERVER_H 3 | #define METEO_GRPC_SERVER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "assert.h" 13 | 14 | #include 15 | 16 | #include "helloworld.grpc.pb.h" 17 | 18 | using grpc::Server; 19 | using grpc::ServerAsyncResponseWriter; 20 | using grpc::ServerAsyncWriter; 21 | using grpc::ServerAsyncReader; 22 | using grpc::ServerAsyncReaderWriter; 23 | using grpc::ServerBuilder; 24 | using grpc::ServerContext; 25 | using grpc::ServerCompletionQueue; 26 | using grpc::Status; 27 | using helloworld::HelloRequest; 28 | using helloworld::HelloReply; 29 | using helloworld::Greeter; 30 | 31 | 32 | class CommonCallData 33 | { 34 | public: 35 | // The means of communication with the gRPC runtime for an asynchronous 36 | // server. 37 | Greeter::AsyncService* service_; 38 | // The producer-consumer queue where for asynchronous server notifications. 39 | ServerCompletionQueue* cq_; 40 | // Context for the rpc, allowing to tweak aspects of it such as the use 41 | // of compression, authentication, as well as to send metadata back to the 42 | // client. 43 | ServerContext ctx_; 44 | // What we get from the client. 45 | HelloRequest request_; 46 | // What we send back to the client. 47 | HelloReply reply_; 48 | // Let's implement a tiny state machine with the following states. 49 | enum CallStatus { CREATE, PROCESS, FINISH }; 50 | CallStatus status_; // The current serving state. 51 | std::string prefix; 52 | 53 | public: 54 | explicit CommonCallData(Greeter::AsyncService* service, ServerCompletionQueue* cq): 55 | service_(service), cq_(cq),status_(CREATE),prefix("Hello ") 56 | {} 57 | 58 | virtual ~CommonCallData() 59 | { 60 | // std::cout << "CommonCallData destructor" << std::endl; 61 | } 62 | 63 | virtual void Proceed(bool = true) = 0; 64 | }; 65 | 66 | class CallData: public CommonCallData 67 | { 68 | ServerAsyncResponseWriter responder_; 69 | public: 70 | CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq): 71 | CommonCallData(service, cq), responder_(&ctx_){Proceed();} 72 | 73 | virtual void Proceed(bool = true) override 74 | { 75 | if (status_ == CREATE) 76 | { 77 | std::cout << "[Proceed11]: New responder for 1-1 mode" << std::endl; 78 | status_ = PROCESS; 79 | service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_, this); 80 | } 81 | else if (status_ == PROCESS) 82 | { 83 | new CallData(service_, cq_); 84 | std::cout << "[Proceed11]: request message = " << request_.name() << std::endl; 85 | reply_.set_message(prefix + request_.name()); 86 | status_ = FINISH; 87 | responder_.Finish(reply_, Status::OK, this); 88 | } 89 | else 90 | { 91 | GPR_ASSERT(status_ == FINISH); 92 | std::cout << "[Proceed11]: Good Bye" << std::endl; 93 | delete this; 94 | } 95 | } 96 | }; 97 | 98 | 99 | class CallData1M: public CommonCallData 100 | { 101 | ServerAsyncWriter responder_; 102 | unsigned mcounter; 103 | bool new_responder_created; 104 | public: 105 | CallData1M(Greeter::AsyncService* service, ServerCompletionQueue* cq): 106 | CommonCallData(service, cq), responder_(&ctx_), mcounter(0), new_responder_created(false){ Proceed() ;} 107 | 108 | virtual void Proceed(bool = true) override 109 | { 110 | if(status_ == CREATE) 111 | { 112 | std::cout << "[Proceed1M]: New responder for 1-M mode" << std::endl; 113 | service_->RequestGladToSeeMe(&ctx_, &request_, &responder_, cq_, cq_, this); 114 | status_ = PROCESS ; 115 | } 116 | else if(status_ == PROCESS) 117 | { 118 | if(!new_responder_created) 119 | { 120 | new CallData1M(service_, cq_); 121 | new_responder_created = true ; 122 | std::cout << "[Proceed1M]: request message = " << request_.name() << std::endl; 123 | } 124 | std::vector greeting = {std::string(prefix + request_.name() + "!"), 125 | "I'm very glad to see you!", 126 | "Haven't seen you for thousand years.", 127 | "I'm server now. Call me later."}; 128 | 129 | std::cout << "[Proceed1M]: mcounter = " << mcounter << std::endl; 130 | if(ctx_.IsCancelled() || mcounter >= greeting.size()) 131 | { 132 | std::cout << "[Proceed1M]: Trying finish" << std::endl; 133 | status_ = FINISH; 134 | responder_.Finish(Status(), (void*)this); 135 | } 136 | else 137 | { 138 | reply_.set_message(greeting.at(mcounter)); 139 | std::cout << "[Proceed1M]: Writing" << std::endl; 140 | responder_.Write(reply_, (void*)this); 141 | ++mcounter; 142 | } 143 | } 144 | else if(status_ == FINISH) 145 | { 146 | std::cout << "[Proceed1M]: Good Bye" << std::endl; 147 | delete this; 148 | } 149 | } 150 | }; 151 | 152 | 153 | class CallDataM1: public CommonCallData 154 | { 155 | ServerAsyncReader responder_; 156 | bool new_responder_created; 157 | public: 158 | CallDataM1(Greeter::AsyncService* service, ServerCompletionQueue* cq): 159 | CommonCallData(service, cq), responder_(&ctx_), new_responder_created(false){Proceed();} 160 | 161 | virtual void Proceed(bool ok = true) override 162 | { 163 | if(status_ == CREATE) 164 | { 165 | std::cout << "[ProceedM1]: New responder for M-1 mode" << std::endl; 166 | status_ = PROCESS ; 167 | service_->RequestGladToSeeYou(&ctx_, &responder_, cq_, cq_, this); 168 | } 169 | else if(status_ == PROCESS) 170 | { 171 | if(!new_responder_created) 172 | { 173 | new CallDataM1(service_, cq_); 174 | new_responder_created = true ; 175 | } 176 | //It's time to send reply 177 | if(!ok) 178 | { 179 | std::string greeting("Hello, Client!"); 180 | reply_.set_message(greeting); 181 | std::cout << "[ProceedM1]: Sending reply" << std::endl; 182 | status_ = FINISH; 183 | responder_.Finish(reply_, Status(), (void*)this); 184 | } 185 | else 186 | { 187 | responder_.Read(&request_, (void*)this); 188 | if(!request_.name().empty()) 189 | std::cout << "[ProceedM1]: request message =" << request_.name() << std::endl; 190 | } 191 | } 192 | else 193 | { 194 | std::cout << "[ProceedM1]: Good Bye" << std::endl; 195 | delete this; 196 | } 197 | } 198 | }; 199 | 200 | 201 | class CallDataMM: public CommonCallData 202 | { 203 | ServerAsyncReaderWriter responder_; 204 | unsigned mcounter; 205 | bool writing_mode_; 206 | bool new_responder_created; 207 | public: 208 | CallDataMM(Greeter::AsyncService* service, ServerCompletionQueue* cq): 209 | CommonCallData(service, cq), responder_(&ctx_), mcounter(0), writing_mode_(false), new_responder_created(false){Proceed();} 210 | 211 | virtual void Proceed(bool ok = true) override 212 | { 213 | if(status_ == CREATE) 214 | { 215 | std::cout << "[ProceedMM]: New responder for M-M mode" << std::endl; 216 | status_ = PROCESS ; 217 | service_->RequestBothGladToSee(&ctx_, &responder_, cq_, cq_, this); 218 | } 219 | else if(status_ == PROCESS) 220 | { 221 | if(!new_responder_created) 222 | { 223 | new CallDataMM(service_, cq_); 224 | new_responder_created = true ; 225 | } 226 | if(!writing_mode_)//reading mode 227 | { 228 | if(!ok) 229 | { 230 | writing_mode_ = true; 231 | ok = true; 232 | std::cout << "[ProceedMM]: changing state to writing" << std::endl; 233 | } 234 | else 235 | { 236 | responder_.Read(&request_, (void*)this); 237 | if(!request_.name().empty()) 238 | std::cout << "[ProceedMM]: request message =" << request_.name() << std::endl; 239 | } 240 | } 241 | if(writing_mode_)//writing mode 242 | { 243 | std::vector greeting = {std::string(prefix + "client" "!"), 244 | "I'm very glad to see you!", 245 | "Haven't seen you for thousand years.", 246 | "How are you?", 247 | "I'm server now. Call me later."}; 248 | if(!ok || mcounter >= greeting.size() || ctx_.IsCancelled()) 249 | { 250 | std::cout << "[ProceedMM]: Trying finish" << std::endl; 251 | status_ = FINISH; 252 | responder_.Finish(Status(), (void*)this); 253 | } 254 | else 255 | { 256 | reply_.set_message(greeting.at(mcounter)); 257 | responder_.Write(reply_, (void*)this); 258 | ++mcounter; 259 | } 260 | } 261 | 262 | } 263 | else 264 | { 265 | std::cout << "[ProceedMM]: Good Bye" << std::endl; 266 | delete this; 267 | } 268 | } 269 | }; 270 | 271 | 272 | 273 | class ServerImpl 274 | { 275 | public: 276 | ~ServerImpl() 277 | { 278 | server_->Shutdown(); 279 | // Always shutdown the completion queue after the server. 280 | cq_->Shutdown(); 281 | } 282 | 283 | void Run() 284 | { 285 | std::string server_address("0.0.0.0:50051"); 286 | 287 | ServerBuilder builder; 288 | // Listen on the given address without any authentication mechanism. 289 | builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); 290 | // Register "service_" as the instance through which we'll communicate with 291 | // clients. In this case it corresponds to an *asynchronous* service. 292 | builder.RegisterService(&service_); 293 | // Get hold of the completion queue used for the asynchronous communication 294 | // with the gRPC runtime. 295 | cq_ = builder.AddCompletionQueue(); 296 | // Finally assemble the server. 297 | server_ = builder.BuildAndStart(); 298 | std::cout << "Server listening on " << server_address << std::endl; 299 | 300 | // Proceed to the server's main loop. 301 | // Spawn a new CallData instance to serve new clients. 302 | new CallData(&service_, cq_.get()); 303 | new CallData1M(&service_, cq_.get()); 304 | new CallDataM1(&service_, cq_.get()); 305 | new CallDataMM(&service_, cq_.get()); 306 | 307 | void* tag; // uniquely identifies a request. 308 | bool ok; 309 | while(true) 310 | { 311 | GPR_ASSERT(cq_->Next(&tag, &ok)); 312 | CommonCallData* calldata = static_cast(tag); 313 | calldata->Proceed(ok); 314 | } 315 | } 316 | 317 | private: 318 | std::unique_ptr cq_; 319 | Greeter::AsyncService service_; 320 | std::unique_ptr server_; 321 | }; 322 | 323 | 324 | int main(int argc, char* argv[]) 325 | { 326 | ServerImpl server; 327 | server.Run(); 328 | } 329 | 330 | 331 | #endif 332 | -------------------------------------------------------------------------------- /helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | option java_multiple_files = true; 18 | option java_package = "io.grpc.examples.helloworld"; 19 | option java_outer_classname = "HelloWorldProto"; 20 | option objc_class_prefix = "HLW"; 21 | 22 | package helloworld; 23 | 24 | // The greeting service definition. 25 | service Greeter { 26 | // Sends a greeting 27 | rpc SayHello (HelloRequest) returns (HelloReply) {} 28 | rpc GladToSeeMe (HelloRequest) returns (stream HelloReply) {} 29 | rpc GladToSeeYou (stream HelloRequest) returns (HelloReply) {} 30 | rpc BothGladToSee (stream HelloRequest) returns (stream HelloReply) {} 31 | } 32 | 33 | // The request message containing the user's name. 34 | message HelloRequest { 35 | string name = 1; 36 | } 37 | 38 | // The response message containing the greetings 39 | message HelloReply { 40 | string message = 1; 41 | } 42 | --------------------------------------------------------------------------------