满纸荒唐言,一把心酸泪,都云作者痴,谁解其中味。 技术博客 心情随笔
gRPC基础:C++服务端与客户端代码示例
2025/1/21 669

导航

1前言

2编译运行于Windows平台的gRPC

3Protocol Buffers介绍

4编写proto文件

5C++服务端

6C++客户端

7后记

1 前言

RPC全称Remote Procedure Call(远程过程调用),它能让调用远端的函数就像调用本地函数一样容易。gRPC就是Google开发的RPC,软件巨头们几乎都开发过RPC框架,像微软的.Net Remoting、WCF,Fackbook的Thrift,阿里的Dubbo以及互联网兴起时的WebService等。但从性能、适用范围、流行程度多个维度来看,截止发文时止,gRPC目前全面领先于其它框架。

gRPC简介

gRPC 使用语言中立的 Protocol Buffers 作为IDL(Interface Definition Language,接口定义语言),然后根据IDL自动生成服务端与客户端的代码。对开发人员来说,只需要使用Protocol Buffers定义函数以及函数使用的数据,gRPC自动生成服务端写客户端的代码,并完成通信(基于HTTP/2)的技术细节,使得开发人员可以把精力聚集于业务。

Protocol Buffers类似于JSON,是一种平台中立、语言中立的数据描述语言,它序列化传输效率比JSON更高。来源:https://www.wubayue.com

2 编译运行于Windows平台的gRPC

使用Git拉取源代码

Git官方仓库 克隆gRPC的源代码,然后更新子模块,因为gRPC依赖了较多的三方库,这个过程会比较慢:

gRPC更新子模块 gRPC子模块

安装Go

在Go语言官网下载对应的Windows版本安装包,并完成安装:https://golang.google.cn/

安装Strawberry Perl

在Strawberry Perl官网下载对应的Windows版本安装包,并完成安装:https://strawberryperl.com/

安装NASM

在NASM官网下载对应的Windows版本安装包,并完成安装(将NASM安装路径配置在系统变量Path中):https://www.nasm.us/

安装CMake

在CMake官网下载对应的Windows版本安装包,并完成安装:https://cmake.org/

编译

在根目录中创建.build子目录,并在.build中执行cmake命令生成Visual Studio解决方案:

cmake .. -G "Visual Studio 17 2022" -A x64/Win32 -DgRPC_INSTALL=ON 

使用管理员权限运行Visual Studio,打开grpc解决方案,编译 ALL_BUILD 项目(耗时较长):

在Visual Studio中编译gRPC

解决“无法打开包括文件stdalign.h”的错误(可能出现):

解决无法打开包括文件stdalign.h的错误

如果处于离线环境,也可以手动下载安装 最新的Windows SDK ,然后批量将项目的Windows SDK版本设置为新的版本号:

解决无法打开包括文件stdalign.h的错误

解决如下代码编译出错的问题(可能出现):

const float kUpb_FltInfinity = INFINITY;
const double kUpb_Infinity = INFINITY;
const double kUpb_NaN = NAN; 

替换为:

const float kUpb_FltInfinity = (float)(1.0 / 0.0);
const double kUpb_Infinity = 1.0 / 0.0;
const double kUpb_NaN = 0.0 / 0.0; 

安装

编译 INSTALL 项目进行安装,编译成功后gRPC默认安装在C:/Program Files/grpc目录中:来源:https://www.wubayue.com

Windows平台安装gRPC

3 Protocol Buffers介绍

Protocol Buffers数据类型

数据类型 描述
bool 布尔类型
string 字符串类型,支持UTF-8或ASCII文本,最大长度不能超过232
bytes 字节流,最大长度不超过232
float 单精度浮点类型
double 双精度浮点类型
int32 32位整型,使用可变长度编码,如果包含负数建议使用sint32以提升效率。
int64 64位整型,使用可变长度编码,如果包含负数建议使用sint64以提升效率。
uint32 无符号32位整型,使用可变长度编码。
uint64 无符号64位整型,使用可变长度编码。
sint32 有符号32位整型,使用可变长度编码,在编码负数时比int32效率更高。
sint64 有符号64位整型,使用可变长度编码,在编码负数时比int64效率更高。
fixed32 无符号32位整型,使用固定4字节长度编码,如果值大于228,则效率高于uint32。
fixed64 无符号64位整型,使用固定8字节长度编码,如果值大于256,则效率高于uint64。
sfixed32 有符号32位整型,使用4字节固定长度编码。
sfixed64 有符号64位整型,使用8字节固定长度编码。

Protocol Buffers中的枚举

enum Colors {
    COLOR_UNKNOW = 0;
    COLOR_RED = 1;
    COLOR_BLUE = 2;
    COLOR_GREEN = 3;
} 

枚举中第一个项的值必须为0。来源:https://www.wubayue.com

Protocol Buffers中的结构体

message Person {
    string Name = 1;
    int Sex = 2;
    int Ages = 3;
}

4 编写proto文件

proto文件作为服务端与客户端的中间契约,在proto文件中定义函数、枚举、结构体等,然后根据proto文件自动生成不同语言的服务端与客户端代码。

在项目中创建一个Protobuf文件夹,用于存放编写的的proto文件,然后拷贝 gRPC安装目录/include/google/ 文件夹到创建的Protobuf目录中,google文件夹中包含了一些通过的.proto文件在我们编写.proto时需要用到,比如对基础数据类型的封装等。

gRPC安装目录/bin/ 文件夹中的protoc.exe、grpc_cpp_plugin.exe、grpc_csharp_plugin.exe三个文件拷贝至GrpcProtobuf文件夹中。它们用于将proto文件生成目标编程语言的源代码。

到此为止,准备工作就绪,参照 原始业务代码 编写proto文件如下:来源:https://www.wubayue.com

// 指定版本
syntax = "proto3";
// 引用公共的数据类型
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";
// 命名空间
package GrpcDemo;

// 性别
enum Sex_Grpc {
    SEX_UNKNOW_Grpc = 0;
    SEX_MALE_Grpc = 1;
    SEX_FEMALE_Grpc = 2;
} 

// 课程
message Course_Grpc {
    int32 courseID = 1;
    string name = 2;
}

// 教师
message Teacher_Grpc {
    int32 teacherID = 1;
    string name = 2;
    Sex_Grpc sex = 3;
    Course_Grpc course = 4;
}

// 学生
message Student_Grpc {
    int32 studentID = 1;
    string name = 2;
    Sex_Grpc sex = 3;
    Teacher_Grpc teacher = 4;
    repeated Course_Grpc courses = 5;
}

// 根据课程ID获取课程信息响应
message getCourse_Response {
    Course_Grpc course = 1;
}

// 根据教师ID获取教师信息响应
message getTeacher_Response {
    Teacher_Grpc teacher = 1;
}

// 获取开设的所有课程信息响应
message getStudents_Response {
    repeated Student_Grpc students = 1;
}

service School_Grpc {
    // 根据课程ID获取课程信息
    rpc getCourse(google.protobuf.UInt32Value) returns (getCourse_Response) {}
    // 根据教师ID获取教师信息
    rpc getTeacher(google.protobuf.UInt32Value) returns (getTeacher_Response) {}
    // 获取所有学生信息
    rpc getStudents(google.protobuf.Empty) returns (getStudents_Response) {}
} 

5 C++服务端

生成gRPC框架代码

protoc --proto_path=./ --cpp_out=./gen_cpp --grpc_out=./gen_cpp --plugin=protoc-gen-grpc=./grpc_cpp_plugin.exe School_Grpc.proto

将生成的 School_Grpc.pb.h、School_Grpc.pb.cc、School_Grpc.grpc.pb.h、School_Grpc.grpc.pb.cc 四个文件拷贝至项目中。

项目配置

gRPC安装目录/bin/ 文件夹中的 zlib.dll 文件拷贝至项目编译输出目录,在项目编译过程中需要使用该文件。

在“项目属性 > C/C++ > 常规 > 附加包含目录”中增加 gRPC安装目录/include/

在“项目属性 > 链接器 > 常规 > 附加库目录”中增加 gRPC安装目录/lib/ ;在“项目属性 > 链接器 > 输入 > 附加依赖项”中将所有.lib文件配置进来。因文件较多,可使用“dir *.lib /b > libs.txt”命令将所有文件名写入文本文件,然后再拷贝至配置中:

在Visual Studio中配置gRPC lib

原始业务代码

原始业务代码中简单描述了一所学校中老师、学生与课程之间的关系( School_Grpc.proto 中定义的内容与业务代码相一致),School.h:

#pragma once
#include <iostream>
#include <vector>

namespace GrpcServer
{
    // 性别
    enum class Sex
    {
        SEX_MALE = 1,
        SEX_FEMALE = 2
    };

    // 课程
    typedef struct Course
    {
        // 课程ID
        int courseID;
        // 课程名称
        std::string name;

        Course() {}
        Course(int courseID, const std::string& name)
        {
            this->courseID = courseID;
            this->name = name;
        }

    } Course;

    // 教师
    typedef struct Teacher
    {
        // 教师ID
        int teacherID;
        // 教师姓名
        std::string name;
        // 教师性别
        Sex sex;
        // 主授课程
        Course course;

        Teacher() {}
        Teacher(const int teacherID, const std::string& name, const Sex sex, const Course& course)
        {
            this->teacherID = teacherID;
            this->name = name;
            this->sex = sex;
            this->course = course;
        }
    } Teacher;

    // 学生
    typedef struct Student
    {
        // 学生ID
        int studentID;
        // 学生姓名
        std::string name;
        // 学生性别
        Sex sex;
        // 导师
        Teacher teacher;
        // 选修课程
        std::vector<Course> courses = {};

        Student() {}
        Student(const int studentID, const std::string& name, const Sex sex, const Teacher& teacher, const std::vector<Course>& courses)
        {
            this->studentID = studentID;
            this->name = name;
            this->sex = sex;
            this->teacher = teacher;
            this->courses.clear();
            for (size_t i = 0; i < courses.size(); i++)
            {
                this->courses.push_back(courses[i]);
            }
        }

    } Student;

    // 学校
    class School
    {
    public:
        School();
        ~School();
        // 根据课程ID获取课程信息
        void getCourse(const int courseID, Course& course);
        // 根据教师ID获取教师信息
        void getTeacher(const int teacherID, Teacher& teacher);
        // 获取所有学生信息
        void getStudents(std::vector<Student>& students);

    private:
        std::vector<Course> _courses;
        std::vector<Teacher> _teachers;
        std::vector<Student> _students;
    };
} 

School.cpp:

#include "School.h"

namespace GrpcServer
{
    School::School()
    {
        // 课程初始化
        this->_courses.clear();
        this->_courses.push_back(Course(1, "Chinese"));
        this->_courses.push_back(Course(2, "Mathematics"));
        this->_courses.push_back(Course(3, "English"));

        // 教师初始化
        this->_teachers.clear();
        Course course1;
        this->getCourse(1, course1);
        this->_teachers.push_back(Teacher(1, "Li Lei", Sex::SEX_MALE, course1));
        Course course2;
        this->getCourse(2, course1);
        this->_teachers.push_back(Teacher(2, "Han Meimei", Sex::SEX_MALE, course2));

        // 学生初始化
        _students.clear();
        // 导师
        Teacher teacher;
        this->getTeacher(1, teacher);
        // 选修课程
        std::vector<Course> courses;
        Course course;
        getCourse(1, course);
        courses.push_back(course);
        _students.push_back(Student(1, "Wu Ba Yue", Sex::SEX_MALE, teacher, courses));
    }
    School::~School()
    {
        this->_courses.clear();
        this->_teachers.clear();
        this->_students.clear();
    }

    void School::getCourse(const int courseID, Course& course)
    {
        if (courseID <= 0 || _courses.size() == 0)
            return;

        for (size_t i = 0; i < _courses.size(); i++)
        {
            if (courseID == _courses[i].courseID)
            {
                course.courseID = _courses[i].courseID;
                course.name = _courses[i].name;
                return;
            }
        }
    }

    void School::getTeacher(const int teacherID, Teacher& teacher)
    {
        if (teacherID <= 0 || _teachers.size() == 0)
            return;

        for (size_t i = 0; i < _teachers.size(); i++)
        {
            if (teacherID == _teachers[i].teacherID)
            {
                teacher.teacherID = _teachers[i].teacherID;
                teacher.name = _teachers[i].name;
                teacher.sex = _teachers[i].sex;
                teacher.course = _teachers[i].course;
                return;
            }
        }
    }

    void School::getStudents(std::vector<Student>& students)
    {
        students.clear();
        for (size_t i = 0; i < _students.size(); i++)
        {
            students.push_back(_students[i]);
        }
    }
} 

Grpc服务结合业务逻辑

创建一个 SchoolGrpcService 类,让它承继自通过.proto自动生成的 School_Grpc::Service 类,然后通过重写 School_Grpc::Service 中的方法,将业务逻辑与Grpc服务相结合。SchoolGrpcService.h:

#pragma once
#include "School_Grpc.grpc.pb.h"
#include "School.h"

using grpc::Server;
using namespace google::protobuf;
using namespace GrpcDemo;

namespace GrpcServer
{
    // 通过继承并重写 School_Grpc::Service 中的方法,将业务逻辑与Grpc服务相结合
    class SchoolGrpcService final : public School_Grpc::Service
    {
#pragma region 数据转换

    public:
        static Sex_Grpc Sex_ToGrpc(const Sex sex);
        static Sex Sex_FromGrpc(const Sex_Grpc sex_Grpc);

        static bool Course_ToGrpc(const Course& course, Course_Grpc& course_Grpc);
        static bool Course_FromGrpc(const Course_Grpc& course_Grpc, Course& course);

        static bool Teacher_ToGrpc(const Teacher& teacher, Teacher_Grpc& teacher_Grpc);
        static bool Teacher_FromGrpc(const Teacher_Grpc& teacher_Grpc, Teacher& teacher);

        static bool Student_ToGrpc(const Student& student, Student_Grpc& student_Grpc);
        static bool Student_FromGrpc(const Student_Grpc& student_Grpc, Student& student);

#pragma endregion

#pragma region 构造与析构

    private:
        School* _school;
    public:
        SchoolGrpcService(School* school);
        ~SchoolGrpcService();

#pragma endregion

#pragma region 业务逻辑

    public:
        // 根据课程ID获取课程信息
        ::grpc::Status getCourse(::grpc::ServerContext* context, const ::google::protobuf::UInt32Value* request, ::GrpcDemo::getCourse_Response* response) override;
        // 根据教师ID获取教师信息
        ::grpc::Status getTeacher(::grpc::ServerContext* context, const ::google::protobuf::UInt32Value* request, ::GrpcDemo::getTeacher_Response* response) override;
        // 获取所有学生信息
        ::grpc::Status getStudents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, ::GrpcDemo::getStudents_Response* response) override;

#pragma endregion

    };
} 

gRPC的应用场景通常是先有某些业务逻辑,然后使用gRPC将这些业务逻辑提供给外部调用,因此数据结构(枚举、结构体等)通常会存在两份,业务逻辑中的与.proto中的,两者之间需要进行转换,SchoolGrpcService.cpp:

#include "SchoolGrpcService.h"

namespace GrpcServer
{
#pragma region 数据转换

    Sex_Grpc SchoolGrpcService::Sex_ToGrpc(const Sex sex)
    {
        switch (sex)
        {
            default:
            case Sex::SEX_MALE:
                return Sex_Grpc::SEX_MALE_Grpc;
            case Sex::SEX_FEMALE:
                return Sex_Grpc::SEX_FEMALE_Grpc;
        }
    }
    Sex SchoolGrpcService::Sex_FromGrpc(const Sex_Grpc sex_Grpc)
    {
        switch (sex_Grpc)
        {
            default:
            case Sex_Grpc::SEX_MALE_Grpc:
                return Sex::SEX_MALE;
            case Sex_Grpc::SEX_FEMALE_Grpc:
                return Sex::SEX_FEMALE;
        }
    }

    bool SchoolGrpcService::Course_ToGrpc(const Course& course, Course_Grpc& course_Grpc)
    {
        course_Grpc.set_courseid(course.courseID);
        course_Grpc.set_name(course.name);
        return true;
    }
    bool SchoolGrpcService::Course_FromGrpc(const Course_Grpc& course_Grpc, Course& course)
    {
        course.courseID = course_Grpc.courseid();
        course.name = course_Grpc.name();
        return true;
    }

    bool SchoolGrpcService::Teacher_ToGrpc(const Teacher& teacher, Teacher_Grpc& teacher_Grpc)
    {
        teacher_Grpc.set_teacherid(teacher.teacherID);
        teacher_Grpc.set_name(teacher.name);
        teacher_Grpc.set_sex(Sex_ToGrpc(teacher.sex));
        // 结构体成员赋值方式
        Course_Grpc course_Grpc;
        Course_ToGrpc(teacher.course, course_Grpc);
        teacher_Grpc.mutable_course()->CopyFrom(course_Grpc);
        return true;
    }
    bool SchoolGrpcService::Teacher_FromGrpc(const Teacher_Grpc& teacher_Grpc, Teacher& teacher)
    {
        teacher.teacherID = teacher_Grpc.teacherid();
        teacher.name = teacher_Grpc.name();
        teacher.sex = Sex_FromGrpc(teacher_Grpc.sex());
        Course_FromGrpc(teacher_Grpc.course(), teacher.course);
        return true;
    }

    bool SchoolGrpcService::Student_ToGrpc(const Student& student, Student_Grpc& student_Grpc)
    {
        student_Grpc.set_studentid(student.studentID);
        student_Grpc.set_name(student.name);
        student_Grpc.set_sex(Sex_ToGrpc(student.sex));
        // 结构体成员赋值方式
        Teacher_Grpc teacher_Grpc;
        Teacher_ToGrpc(student.teacher, teacher_Grpc);
        student_Grpc.mutable_teacher()->CopyFrom(teacher_Grpc);
        // vector成员赋值方式
        student_Grpc.clear_courses();
        if (student.courses.size() > 0)
        {
            std::vector<Course_Grpc> vecCourseGrpc;
            for (size_t i = 0; i < student.courses.size(); i++)
            {
                Course_Grpc course_Grpc;
                Course_ToGrpc(student.courses[i], course_Grpc);
                vecCourseGrpc.push_back(course_Grpc);
            }
            student_Grpc.mutable_courses()->Add(vecCourseGrpc.begin(), vecCourseGrpc.end());
        }
        return true;
    }
    bool SchoolGrpcService::Student_FromGrpc(const Student_Grpc& student_Grpc, Student& student)
    {
        student.studentID = student_Grpc.studentid();
        student.name = student_Grpc.name();
        student.sex = Sex_FromGrpc(student_Grpc.sex());
        Teacher_FromGrpc(student_Grpc.teacher(), student.teacher);
        student.courses.clear();
        for (size_t i = 0; i < student_Grpc.courses_size(); i++)
        {
            Course course;
            Course_FromGrpc(student_Grpc.courses(i), course);
            student.courses.push_back(course);
        }
        return true;
    }

#pragma endregion

#pragma region 构造与析构

    SchoolGrpcService::SchoolGrpcService(School* school)
    {
        this->_school = school;
    }
    SchoolGrpcService::~SchoolGrpcService()
    {
        if (this->_school != nullptr)
            this->_school = nullptr;
    }

#pragma endregion

#pragma region 业务逻辑

    ::grpc::Status SchoolGrpcService::getCourse(::grpc::ServerContext* context, const ::google::protobuf::UInt32Value* request, ::GrpcDemo::getCourse_Response* response)
    {
        if (this->_school != nullptr)
        {
            // 调用业务逻辑
            Course course;
            this->_school->getCourse(request->value(), course);
            // 结果转换
            Course_Grpc course_Grpc;
            Course_ToGrpc(course, course_Grpc);
            // 结果返回
            response->mutable_course()->CopyFrom(course_Grpc);
        }
        return ::grpc::Status::OK;
    }
    ::grpc::Status SchoolGrpcService::getTeacher(::grpc::ServerContext* context, const ::google::protobuf::UInt32Value* request, ::GrpcDemo::getTeacher_Response* response)
    {
        if (this->_school != nullptr)
        {
            // 调用业务逻辑
            Teacher teacher;
            this->_school->getTeacher(request->value(), teacher);
            // 结果转换
            Teacher_Grpc teacher_Grpc;
            Teacher_ToGrpc(teacher, teacher_Grpc);
            // 结果返回
            response->mutable_teacher()->CopyFrom(teacher_Grpc);
        }
        return ::grpc::Status::OK;
    }
    ::grpc::Status SchoolGrpcService::getStudents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, ::GrpcDemo::getStudents_Response* response)
    {
        if (this->_school != nullptr)
        {
            // 调用业务逻辑
            std::vector<Student> students;
            this->_school->getStudents(students);
            // 结果转换
            std::vector<Student_Grpc> vecStudentsGrpc;
            if (students.size() > 0)
            {
                for (size_t i = 0; i < students.size(); i++)
                {
                    Student_Grpc student_Grpc;
                    Student_ToGrpc(students[i], student_Grpc);
                    vecStudentsGrpc.push_back(student_Grpc);
                }
            }
            // 结果返回
            response->clear_students();
            if (vecStudentsGrpc.size() > 0)
                response->mutable_students()->Add(vecStudentsGrpc.begin(), vecStudentsGrpc.end());
        }
        return ::grpc::Status::OK;
    }

#pragma endregion
} 

开启gRPC服务

gRPC服务在单独的线程中监听指定的端口,通过Http协议进行通信,GrpcService.h:

#pragma once
#include <iostream>
#include <grpcpp/grpcpp.h>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/health_check_service_interface.h>
#include "SchoolGrpcService.h"

namespace GrpcServer
{
    class GrpcService
    {
    public:
        GrpcService();
        ~GrpcService();

        // 启动Grpc服务
        bool start();

    private:
        School* _pSchool;
        SchoolGrpcService* _pSchoolGrpcService;
        // gRPC服务线程相关
        bool _isGrpcServiceStarted;
        std::shared_ptr<std::thread> _pGrpcServiceThread;
        void grpcServiceThread();
    };
} 

GrpcService.cpp:来源:https://www.wubayue.com

#include "GrpcService.h";

namespace GrpcServer
{
    GrpcService::GrpcService()
    {
        _pSchool = new School();
        _pSchoolGrpcService = new SchoolGrpcService(_pSchool);
        _isGrpcServiceStarted = false;
    }
    GrpcService::~GrpcService()
    {
        if (_pSchoolGrpcService != nullptr)
        {
            delete _pSchoolGrpcService;
            _pSchoolGrpcService = nullptr;
        }
        if (_pSchool != nullptr)
        {
            delete _pSchool;
            _pSchool = nullptr;
        }
        _isGrpcServiceStarted = false;
    }

    bool GrpcService::start()
    {
        if (_isGrpcServiceStarted)
            return false;
        // 在单独的线程中运行gRPC服务
        _pGrpcServiceThread = std::make_shared<std::thread>(&GrpcService::grpcServiceThread, this);
        _isGrpcServiceStarted = true;
        return true;
    }

    void GrpcService::grpcServiceThread()
    {
        grpc::EnableDefaultHealthCheckService(true);
        grpc::reflection::InitProtoReflectionServerBuilderPlugin();
        grpc::ServerBuilder builder;
        // 在所有IP段监听8888端口
        builder.AddListeningPort("0.0.0.0:8888", grpc::InsecureServerCredentials());
        builder.RegisterService(_pSchoolGrpcService);
        std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
        server->Wait();
    }
}

int main()
{
    GrpcServer::GrpcService grpcService;
    std::cout << "Grpc server start " << (grpcService.start() ? "succeed." : "failed.") << std::endl;
    std::string k;
    std::cin >> k;
    return 0;
} 

6 C++客户端

拷贝gRPC框架代码

将章节5中生成的 School_Grpc.pb.h、School_Grpc.pb.cc、School_Grpc.grpc.pb.h、School_Grpc.grpc.pb.cc 四个文件拷贝至项目中。

项目配置

客户端项目配置与服务端完全相同,将 gRPC安装目录/bin/ 文件夹中的 zlib.dll 文件拷贝至项目编译输出目录,在项目编译过程中需要使用该文件。

在“项目属性 > C/C++ > 常规 > 附加包含目录”中增加 gRPC安装目录/include/

在“项目属性 > 链接器 > 常规 > 附加库目录”中增加 gRPC安装目录/lib/ ;在“项目属性 > 链接器 > 输入 > 附加依赖项”中将所有.lib文件配置进来。

客户端代码

SchoolGrpcClient.h:

#pragma once
#include <grpcpp/grpcpp.h>
#include "School_Grpc.grpc.pb.h"

namespace GrpcClient
{
    class SchoolGrpcClient
    {
    private:
        std::unique_ptr<GrpcDemo::School_Grpc::Stub> _stub;

    public:
        SchoolGrpcClient(std::shared_ptr<grpc::Channel> channel);
        GrpcDemo::School_Grpc::Stub* getStub();
    };
} 

SchoolGrpcClient.cpp:来源:https://www.wubayue.com

#include "SchoolGrpcClient.h"

namespace GrpcClient
{
    // 创建Stub对象(Stub可理解为服务端的代理)
    SchoolGrpcClient::SchoolGrpcClient(std::shared_ptr<grpc::Channel> channel)
        : _stub(GrpcDemo::School_Grpc::NewStub(channel)) {
    }

    // 对外提供Stub对象
    GrpcDemo::School_Grpc::Stub* SchoolGrpcClient::getStub()
    {
        return _stub.get();
    }
}

int main()
{
    auto channel = grpc::CreateChannel("127.0.0.1:8888", grpc::InsecureChannelCredentials());
    GrpcClient::SchoolGrpcClient client(channel);


    grpc::ClientContext context;
    google::protobuf::Empty request;
    GrpcDemo::getStudents_Response response;

    // 获取Stub对象,调用服务端:获取所有学生信息
    grpc::Status status = client.getStub()->getStudents(&context, request, &response);
    if (status.ok())
    {
        for (size_t i = 0; i < response.students_size(); i++)
        {
            std::cout << "studentID : " << response.students(i).studentid() << std::endl;
            std::cout << "name : " << response.students(i).name() << std::endl;
        }
    }
} 

7 后记

本文详细描述了gRPC在C++环境中的编译安装,展示了如何通过Protocol Buffers编写一个gRPC服务并生成源代码,然后通过代码示例了gRPC的C++服务端与客户端。计划还会补充一篇《gRPC进阶:通过流模式实现观察者模式》,用来说明事件(信号槽)在gRPC中的应用示例,这样对gRPC的介绍就基本完整了。来源:https://www.wubayue.com

<全文完>