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

导航

1前言

2编译运行于Windows平台的gRPC

3Protocol Buffers介绍

4编写proto文件

5C++服务端

6C++客户端

7grpc工具

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()
    {
        // 关闭健康检查服务(该服务会影响grpcui工具调用)
        grpc::EnableDefaultHealthCheckService(false);
        // 开启Reflection发现服务
        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工具

grpcurl

下载:https://github.com/fullstorydev/grpcurl/releases

grpcurl -plaintext GrpcServerIP:Port list 

grpcui

下载:https://github.com/fullstorydev/grpcui/releases

grpcui -plaintext GrpcServerIP:Port 

8 后记

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

<全文完>

赞同 0
反对 0
请 登录 或 注册会员 后发表评论。
评论列表