gRPC实现第一个C++服务器

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和本地版本不一致,需要保证两个版本一致,两个解决方法

  1. 方法一:
    或者卸载掉本地版本,卸载指令
    先查看位置
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 
  1. 方法二
    先检查你的grpc使用的protoc版本,然后安装相应版本
<your grpc install path>/bin/protoc --version
# libprotoc 25.1

故障二:
在这里插入图片描述
使用C++17可以解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值