1. 前言
本章节将实现C++版本的个人注册页面的gRPC服务器
更多基础知识可以参考上一节: GRPC 快速入门
2. 定义服务
要定义服务,需要在login.proto文件中指定一个名为service的内容,另外我们还需要定义一个LoginInfo的消息体用来传递信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| syntax = "proto3"; //指定版本信息,不指定会报错
package tutorial; //package声明符,用来防止不同的消息类型有命名冲突
// 注册消息体 message LoginInfo { string usrname = 1; string password = 2; }
// login response message LoginResponse { // 状态码,0表示成功 int32 status_code = 1; // 返回信息,包含成功或错误描述 string message = 2; }
// 定义服务 service LoginService { rpc login(LoginInfo) returns (LoginResponse); }
|
将.proto 翻译成C++文件
1 2
| protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` login.proto protoc -I . --cpp_out=. login.proto
|
运行此命令将在您的当前目录中生成以下文件:
如果要生成python 文件
1 2 3
| pip3 install grpcio-tools python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. login.proto
|
3. 服务器代码
下面我们来编写服务器代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <grpcpp/grpcpp.h> #include "login.pb.h" #include "login.grpc.pb.h"
class LoginServiceImpl final : public tutorial::LoginService::Service { grpc::Status login(::grpc::ServerContext *context, const ::tutorial::LoginInfo *request, ::tutorial::LoginResponse *response) override { std::cout << " usrname: " << request->usrname() << " password: " << request->password() << std::endl; std::string msg("Login successfully!"); response->set_status_code(0); response->set_message(msg); return grpc::Status::OK; } };
int main(int argv, char **argc) { const std::string server_address = "localhost:50051"; LoginServiceImpl service = LoginServiceImpl();
grpc::ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
return 0; }
|
4. 客户端代码
下面我们来编写客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <grpcpp/grpcpp.h> #include "login.pb.h" #include "login.grpc.pb.h" #include <unordered_map>
std::unordered_map<std::string, std::string> user_maps = { {"张三", "123456"}, {"李四", "666666"}, };
int main(int argc, char **argv) { const std::string server_address = "localhost:50051"; grpc::ChannelArguments channel_args; channel_args.SetMaxReceiveMessageSize(1024 * 1024 * 1024); channel_args.SetMaxSendMessageSize(1024 * 1024 * 1024); auto channel = grpc::CreateCustomChannel( server_address, grpc::InsecureChannelCredentials(), channel_args); std::unique_ptr<tutorial::LoginService::Stub> stub = tutorial::LoginService::NewStub(channel);
for (auto user : user_maps) { tutorial::LoginInfo info; tutorial::LoginResponse res; info.set_usrname(user.first); info.set_password(user.second); grpc::ClientContext context; grpc::Status status = stub->login(&context, info, &res); if (!status.ok()) { std::cerr << "检查网络是否发生错误: " << status.error_message() << std::endl; continue; }
if (!res.status_code() == 0) { std::cerr << user.first << "注册失败: " << res.message() << std::endl; continue; }
std::cerr << user.first << "注册成功: " << res.message() << std::endl; continue; } return 0; }
|
5. 编译
测试过Makefile,编译依赖库太多,不建议使用。建议使用cmake比较好处理, CMakeLists.txt 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| cmake_minimum_required(VERSION 3.8)
project(Tutorial C CXX)
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) find_package(Threads REQUIRED) set(_REFLECTION gRPC::grpc++_reflection) find_package(Protobuf CONFIG REQUIRED) set(_GRPC_GRPCPP gRPC::grpc++) find_package(gRPC CONFIG REQUIRED)
set(PROTO_SRCS login.grpc.pb.cc login.pb.cc)
set(CMAKE_CXX_STANDARD 17)
add_executable(login_client ${PROTO_SRCS} client.cpp) add_executable(login_server ${PROTO_SRCS} server.cpp)
target_link_libraries(login_client absl::flags absl::flags_parse ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF} )
target_link_libraries(login_server absl::flags absl::flags_parse ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF} )
|
1 2
| mkdir build && cd build cmake ..
|
编译服务器和客户端
1
| make login_server login_client
|
先运行服务器,再运行客户端,服务器端看到正常打印时,恭喜你完成了第一个C++通讯程序
6. 故障解决
故障一:
在编译时可能会遇到protocol版本冲突,出现如下报错(error: #error PROTOBUF_VERSION was previously defined):

说明grpc中使用的protocol和本地版本不一致,需要保证两个版本一致,两个解决方法
- 方法一:
或者卸载掉本地版本,卸载指令
先查看位置
删除可执行和库
1 2 3
| rm -rf /usr/local/bin/protoc sudo rm -rf /usr/local/include/google/protobuf sudo rm -rf /usr/local/lib/libproto*
|
配置grpc的bin到path中
1
| export PATH=$PATH:<your grpc install path>/bin
|
然后查看protoc是否是使用的grpc中的protoc
- 方法二
先检查你的grpc使用的protoc版本,然后安装相应版本
1 2
| <your grpc install path>/bin/protoc --version
|
故障二:

使用C++17可以解决