1. 前言
本章节将实现C++版本的个人注册页面的gRPC服务器
更多基础知识可以参考上一节: GRPC 快速入门
2. 定义服务
要定义服务,需要在login.proto文件中指定一个名为service的内容,另外我们还需要定义一个LoginInfo的消息体用来传递信息
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++文件
protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` login.proto
protoc -I . --cpp_out=. login.proto
运行此命令将在您的当前目录中生成以下文件:
- login.pb.h: 声明生成的客户端的头文件
- login.pb.cc: 包含客户端的实现
- login.grpc.pb.h: 声明生成的服务器端的头文件
- login.grpc.pb.cc: 包含服务器端的实现
如果要生成python 文件
# 安装依赖
pip3 install grpcio-tools
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. login.proto
3. 服务器代码
下面我们来编写服务器代码
#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. 客户端代码
下面我们来编写客户端代码
#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); // 1GB
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 如下:
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}
)
mkdir build && cd build
cmake ..
编译服务器和客户端
make login_server login_client
先运行服务器,再运行客户端,服务器端看到正常打印时,恭喜你完成了第一个C++通讯程序
6. 故障解决
故障一:
在编译时可能会遇到protocol版本冲突,出现如下报错(error: #error PROTOBUF_VERSION was previously defined
):
说明grpc中使用的protocol和本地版本不一致,需要保证两个版本一致,两个解决方法
- 方法一:
或者卸载掉本地版本,卸载指令
先查看位置
which protoc
# protoc: /usr/local/bin/protoc
删除可执行和库
rm -rf /usr/local/bin/protoc #可执行
sudo rm -rf /usr/local/include/google/protobuf #头文件
sudo rm -rf /usr/local/lib/libproto* # 库文件
配置grpc的bin到path中
export PATH=$PATH:<your grpc install path>/bin
然后查看protoc是否是使用的grpc中的protoc
which protoc
- 方法二
先检查你的grpc使用的protoc版本,然后安装相应版本
<your grpc install path>/bin/protoc --version
# libprotoc 25.1
故障二:
使用C++17可以解决