VS2015编译gRPC完整指南:版本选型、依赖配置与避坑
2026/9/8 12:48:44 网站建设 项目流程

简介:面向Windows平台上使用Visual Studio 2015进行C++开发的工程师,这份gRPC预编译环境包可直接用于构建微服务与分布式系统。压缩包内含884个文件,包括430个头文件、306个C++源文件、64个proto协议定义文件,以及编译好的18个lib库、8个dll动态库和26个exe工具,整体约72MB,Debug与Release两种配置均已齐备。包内自带可运行的helloworld示例工程,能帮助开发者快速完成gRPC服务的定义、生成与调用流程,降低环境搭建门槛。已有1753人学习下载,适合需要高效上手gRPC C++开发或希望省去编译流程的开发者参考使用。

1. 写在前面:为什么要折腾 VS2015 + gRPC

先说下这次的事。我把一个老项目的通信层从自研的 TCP 私有协议迁到 gRPC,本来想着"官方文档够全,新版本直接编就行",结果一动手就发现卡在第一步:项目是在 VS2015 环境下构建的,而 gRPC 官方从某个版本开始早就放弃了对 VS2015 的官方支持。

你要是搜过 "grpc windows vs2015" 这几个词,大概率和我一样,遇到了两类情况:要么是编译 gRPC 源码时,一堆 C++ 语法层面的报错堆成山;要么是网上那些老教程里的步骤根本跑不通,因为 gRPC 版本的迭代太快,很多旧教程用的还是 protobuf 2.x 和 gRPC 1.x 时代的产物,跟现在的源码树长得完全不一样。

这个事放到今天依然有现实意义。很多工业软件、传统 IT 项目、教学实验环境,用的还是 VS2015(甚至 VS2013),而 gRPC 作为现代 RPC 框架,生态成熟、跨语言能力强、基于 HTTP/2,确实是值得引入的通信方案。这篇博文就是我在 Windows + VS2015 环境下把 gRPC 从源码编译到落地跑通的完整记录,涉及版本选型、依赖库处理、CMake 工程配置、链接坑点和排错实录。适合刚好被"老环境 + 新框架"夹在中间的 C++ 开发者参考。

2. 项目整体设计与版本选型思路

2.1 为什么不能直接"下个最新版就开编"

我先说结论:在 VS2015 下,不要直接用 gRPC 主线最新版

gRPC 在 1.20.x 之后基本放弃了对 MSVC 2015 的官方适配,主线代码要求 VS2017 起,部分新特性甚至要求支持 C++17。而 VS2015 对 C++14 的支持本身就不完整,像if constexprstd::variant、折叠表达式这些新语法,VS2015 的 C++ 编译器(19.00/19.1x)要么不支持,要么支持得七零八落。强行编主线代码,你会看到几百条语法错误,根本没法定位。

所以我这次选型时定了一个核心原则:以 gRPC 1.19.x ~ 1.20.x 为主力版本段。这个区间的 gRPC 仍然保留了对 VS2015 的兼容补丁,protobuf 用 3.8.x 配套版本,能够在不改源码的前提下编出可用的静态库和动态库。

2.2 组件清单与依赖树

gRPC 不是单一库,它有一整套依赖树。我整理了一个选型清单,直接照着抄就行:

组件推荐版本说明
gRPCv1.20.1最后一批官方兼容 VS2015 的版本之一
protobufv3.8.0gRPC 1.20 配套版本,不能随便升
cares内置gRPC 源码里自带,无需单独下载
zlib1.2.11用系统自带或源码目录里的
openssl1.1.1需要单独编译或找预编译包
CMake3.10 ~ 3.15别用太新版,新版 CMake 对 VS2015 生成器支持会减弱

你可能要问:为什么非要从源码编?直接用 vcpkg 或者 conan 不行吗?

答案是可以,但我建议你还是手动编译一遍,原因有两个。第一,vcpkg 默认会拉最新版本,而 VS2015 的兼容版本需要指定--triplet和版本号,实操中经常出现依赖解析不一致的问题。第二,很多企业内网的开发机不能访问 GitHub,手动下载源码包、按依赖顺序编译,是最可控的离线方案。我自己是在外网机器上编译好库文件,把产物拷到内网工程里使用的。

2.3 工具链准备

VS2015 的 C++ 工具链默认安装时不会包含所有组件,需要确认以下几项:

  • VS2015 任意版本(Community 即可,密钥问题网上自己解决,这里不展开),安装时必须勾选"Visual C++"。
  • CMake 3.12 以上的 Windows 安装包,建议 3.13 ~ 3.15 之间。
  • Git for Windows,用来拉取源码和切换分支。
  • ActiveState Perl,或者 Strawberry Perl。这个是为了编译 OpenSSL 用的,VS2015 下 OpenSSL 的Configure脚本依赖 Perl,不装的话编译 OpenSSL 直接报错。
  • Go 语言环境(可选)。gRPC 的某些工具(如grpc_tools)源码编译时可能用到,但如果你只编 C++ 库,这一步可以先跳过。

等你把这些都装完,你的开发机已经是一个可以手动折腾 gRPC 的完整环境了。接下来是关键的编译环节。

3. 核心细节解析:编译顺序与每个库的关键配置

3.1 编译顺序为什么重要

gRPC 的依赖是分层的:zlib 和 openssl 在最底层,c-ares 居中,protobuf 在 gRPC 之上还要生成代码文件,最后才是 gRPC 本体。如果你打乱了顺序,比如先编 gRPC 再编 protobuf,CMake 会创建一堆缓存文件指向不存在的路径,导致后续各种LNK1104 找不到 xxx.lib的问题。

我的建议顺序是:

  1. zlib
  2. OpenSSL
  3. c-ares(可以用 gRPC 源码自带的third_party/cares
  4. protobuf
  5. gRPC

3.2 OpenSSL 编译的坑

在 Windows 下编译 OpenSSL 有两个经典选择:用预编译安装包,或者用源码自己编。我强烈建议不要自己编译 OpenSSL,除非你特别闲。OpenSSL 在 Windows 下编译需要配合 Perl、NASM,还要正确的命令参数,稍有差池就编出一个"半残"的库,调用时各种内存访问错误。

解决方案是直接从 slproweb.com 下载 OpenSSL 1.1.1 的 Win32/Win64 预编译安装包,安装时选择"Copy OpenSSL DLLs to application directory"或手动记录下来安装目录。安装后的lib目录下有libssl.liblibcrypto.lib以及对应的 DLL。

如果你真的出于某种原因必须自己编译 OpenSSL,可以用如下命令序列(假设你已安装 Perl 和 NASM,并且已经打开了 VS2015 的开发者命令行):

perl Configure VC-WIN64A no-asm --prefix=C:\openssl perl Configure VC-WIN64A --prefix=C:\openssl ms\do_win64a.bat nmake nmake install

注意no-asm参数是给那些不想配 NASM 的人准备的,但是会造成性能下降。如果开启汇编,务必确保 NASM 在 PATH 中。

3.3 Protobuf 编译要点

Protobuf 是整个 gRPC 工程里最绕不开的一环。它的源码树既需要编译库文件,还需要编译出protoc.exe编译器。在 gRPC 的依赖中,protobuf 的版本必须和 gRPC 匹配。我用了 protobuf v3.8.0,对应的 CMake 构建指令如下:

git clone -b v3.8.0 https://github.com/protocolbuffers/protobuf.git cd protobuf mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_GENERATOR_PLATFORM=x64 ^ -DCMAKE_INSTALL_PREFIX=C:\grpc-deps\protobuf ^ -Dprotobuf_BUILD_SHARED_LIBS=OFF ^ -Dprotobuf_BUILD_TESTS=OFF cmake --build . --config Release cmake --install .

这里解释几个参数:

  • -DCMAKE_BUILD_TYPE=Release:只编 Release 版本,因为 Debug 版本的 gRPC 依赖也会对应 Debug,否则混链会导致大量运行期错误。
  • -Dprotobuf_BUILD_SHARED_LIBS=OFF:这里用静态库,运行时不用带一大堆 DLL。
  • -DCMAKE_GENERATOR_PLATFORM=x64:指定 64 位。如果你的项目是 Win32(32 位),这个要改成 Win32,且所有依赖库也需要保持 32 位一致。

编译结束后,你会在C:\grpc-deps\protobuf下看到binincludelib三个目录。protoc.exebin下,后面用它生成 C++ 的桩代码。

3.4 gRPC 本体编译:选择你的战术

到了 gRPC 本体这一步,有两种方案。

方案 A(推荐,省心):用官方集成的 CMake 构建

gRPC 官方在 1.20.x 的 CMake 文件里已经集成了对第三方依赖的构建逻辑,只要我们的依赖目录结构能被正确识别到,就可以一条命令编完。具体命令如下:

git clone -b v1.20.1 https://github.com/grpc/grpc.git cd grpc git submodule update --init --recursive mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_GENERATOR_PLATFORM=x64 ^ -DCMAKE_INSTALL_PREFIX=C:\grpc-deps\grpc ^ -DgRPC_INSTALL=ON ^ -DgRPC_BUILD_TESTS=OFF ^ -DgRPC_ZLIB_PROVIDER=package ^ -DgRPC_CARES_PROVIDER=package ^ -DgRPC_SSL_PROVIDER=package ^ -DgRPC_PROTOBUF_PROVIDER=package ^ -DZLIB_ROOT=C:\grpc-deps\zlib ^ -DCARES_ROOT=C:\grpc-deps\cares ^ -DOPENSSL_ROOT_DIR=C:\OpenSSL-Win64 ^ -DProtobuf_ROOT=C:\grpc-deps\protobuf cmake --build . --config Release --target grpc++ cmake --install .

这里有一个很关键的地方:-DgRPC_*_PROVIDER=package表示使用我们手工编译好的依赖包,而不是让 CMake 自动去拉源码重新编译。如果你在配置 CMake 时嫌麻烦,也可以直接使用-DgRPC_*_PROVIDER=module,让 CMake 自己处理third_party里的源码,但这样整体编一次的时间会非常长(我实测在 i5-8400 上跑了将近 40 分钟),而且中间任何一个依赖出现编译失败,整个构建就断了。

方案 B:用 vcpkg 指定版本(如果你能访问 GitHub 且内网不受限)

vcpkg 也提供了一套 VS2015 友好的构建方式,但你必须指定版本号,否则默认拉最新版,直接把自己带进坑里:

vcpkg install grpc:x64-windows-static --overlay-ports=your-overlay

但说实话,我用 vcpkg 编 VS2015 版本的 gRPC 时,踩到的坑比手动编译还多,主要体现在 vcpkg 的 ports 树更新太快,老版本的补丁不一定兼容新的 vcpkg 工具链。所以如果你是第一次尝试,还是老老实实手动编译更靠谱。

4. 实操过程记录与关键验证

4.1 编写 proto 文件与生成桩代码

库编译好了,接下来就是实战环节。我以一个最简的通信测试为例:一个客户端往服务端发送"ping",服务端返回"pong"。

新建hello.proto

syntax = "proto3"; package hello; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }

然后用前面编译出来的protoc.exe和 gRPC 的插件生成 C++ 代码:

C:\grpc-deps\protobuf\bin\protoc.exe ^ --proto_path=. ^ --cpp_out=. ^ --grpc_out=. ^ --plugin=protoc-gen-grpc=C:\grpc-deps\grpc\bin\grpc_cpp_plugin.exe ^ hello.proto

命令跑完,目录下会多出hello.pb.hhello.pb.cchello.grpc.pb.hhello.grpc.pb.cc四个文件。这一步如果报"无法加载插件"或"找不到 protoc-gen-grpc",大概率是插件路径不对,或者只编了 gRPC 库没编插件,回到上一步确认grpc_cpp_plugin.exe是否存在。

4.2 在 VS2015 里创建工程并配置链接

新建一个空的 VS2015 C++ 控制台工程,注意平台选择 x64,然后把四个生成的.cc文件加进工程,再在工程属性里做如下配置(以 Release 配置为例):

  • C/C++ -> 常规 -> 附加包含目录:
    • C:\grpc-deps\grpc\include
    • C:\grpc-deps\protobuf\include
    • C:\grpc-deps\cares\include
    • C:\OpenSSL-Win64\include
    • C:\grpc-deps\zlib\include
  • 链接器 -> 常规 -> 附加库目录:
    • C:\grpc-deps\grpc\lib
    • C:\grpc-deps\protobuf\lib
    • C:\grpc-deps\cares\lib
    • C:\OpenSSL-Win64\lib
    • C:\grpc-deps\zlib\lib
  • 链接器 -> 输入 -> 附加依赖项:手动填写关键库:
    • grpc++.lib
    • grpc.lib
    • gpr.lib
    • protobuf.lib
    • cares.lib
    • libssl.lib
    • libcrypto.lib
    • zlibstat.lib
    • Ws2_32.lib
    • advapi32.lib

这里特别强调一点:VS2015 默认运行库是/MT(多线程静态链接)还是/MD(多线程动态链接),必须和依赖库的编译方式一致。我编依赖库时用的是 CMake 默认(VS 工程默认是/MD),所以你的工程也得是/MD。如果依赖库编译时被改成了/MT,你却用/MD链接,最后会出现LNK2038 运行时库不匹配的报错。

正确的设置入口:工程属性 -> C/C++ -> 代码生成 -> 运行库,选择"多线程 DLL (/MD)"。

4.3 服务端和客户端的核心代码

服务端代码(服务端监听 50051 端口):

#include <iostream> #include <memory> #include <string> #include <grpcpp/grpcpp.h> #include "hello.grpc.pb.h" using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; using hello::HelloRequest; using hello::HelloReply; using hello::Greeter; class GreeterServiceImpl final : public Greeter::Service { Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override { std::string prefix("Hello "); reply->set_message(prefix + request->name()); std::cout << "recv: " << request->name() << std::endl; return Status::OK; } }; int main() { std::string server_address("0.0.0.0:50051"); GreeterServiceImpl service; ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); std::unique_ptr<Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; server->Wait(); return 0; }

客户端代码:

#include <iostream> #include <memory> #include <string> #include <grpcpp/grpcpp.h> #include "hello.grpc.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::Status; using hello::HelloRequest; using hello::HelloReply; using hello::Greeter; class GreeterClient { public: GreeterClient(std::shared_ptr<Channel> channel) : stub_(Greeter::NewStub(channel)) {} std::string SayHello(const std::string& user) { HelloRequest request; request.set_name(user); HelloReply reply; ClientContext context; Status status = stub_->SayHello(&context, request, &reply); if (status.ok()) { return reply.message(); } else { return "RPC failed: " + status.error_message(); } } private: std::unique_ptr<Greeter::Stub> stub_; }; int main() { GreeterClient greeter(grpc::CreateChannel( "localhost:50051", grpc::InsecureChannelCredentials())); std::string user("world"); std::string reply = greeter.SayHello(user); std::cout << "Client received: " << reply << std::endl; return 0; }

编译通过后,先启动服务端,再启动客户端。如果一切正常,服务端控制台输出recv: world,客户端控制台输出Client received: Hello world

5. 常见问题排查与避坑实录

这部分是我这次折腾最值钱的部分。所有问题都经过了实测,按出现频率排序。

5.1 编译时报错 C1083 / C2039 等编译器版本问题

如果你坚持用了 gRPC 1.21 以上版本,在 VS2015 下你会看到大量 C1083、C2039 报错,比如std::result_of相关错误,这是因为新版 gRPC 用了absl(Abseil 库),而 Abseil 要求 VS2017+。这类问题的唯一解法就是换版本,没有捷径。

5.2 LNK1104 找不到 libssl.lib / libcrypto.lib

这个报错十有八九是 OpenSSL 的安装问题。很多人安装 OpenSSL 时选择的是 Win32 版本,但 gRPC 工程是 x64,结果就是库目录配置了但找不到匹配的库。检查方法:右键工程属性,看"链接器 -> 常规 -> 附加库目录"是否指向了 OpenSSL-Win64 的 lib 目录;再确认你的工程平台是 x64,不是 Win32。如果都正确,去C:\OpenSSL-Win64\lib下手动确认文件存在。

5.3 运行时崩溃:应用程序无法正常启动 0xc000007b

这个错误在 Windows 下太经典了。本质原因是 DLL 位数不匹配,比如你的 exe 是 x64 的,但你拷进去的libssl.dll是 32 位的(或者反过来)。检查自己的可执行文件旁边所需的所有 DLL:libcrypto-1_1-x64.dlllibssl-1_1-x64.dllzlib1.dll,确保和 exe 位数一致。

5.4 服务端启动后客户端连接超时

如果你确定代码逻辑没问题、端口没被防火墙拦截,留意一个点:gRPC 在某些 Windows 环境默认使用 IPv6 地址。当服务端监听0.0.0.0:50051,但客户端用localhost:50051连接时,Windows 会把 localhost 解析成::1(IPv6 回环),导致连接失败。解决方案:客户端统一写127.0.0.1:50051,或者服务端监听[::]:50051

5.5 编译时提示 protobuf 版本不匹配

如果你在编译自己项目时看到This file was generated by an older version of protoc或者unsafe to use,说明你的protoc.exe和实际编译用的 protobuf 库版本不一致。回看 3.3 节,确保你用v3.8.0分支编出来的protoc.exe生成桩代码,链接的是同一个版本的protobuf.lib

5.6 VS2015 中 inttypes.h 找不到

这是 VS2015 特有的一个坑:MSVC 直到 VS2017 才提供完整的 C99 头文件支持,而某些第三方库(特别是旧版 c-ares)的源码里引用了<inttypes.h>。解决办法是在工程预定义中添加_CRT_SECURE_NO_WARNINGS,并在stdafx.h或工程里加上下面的兼容声明:

#if defined(_MSC_VER) && _MSC_VER < 1900 typedef signed char int8_t; typedef short int16_t; typedef int int32_t; typedef long long int64_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #endif

5.7 把动态库改成静态库的选项

如果将来你想摆脱 DLL 依赖,把-Dprotobuf_BUILD_SHARED_LIBS=OFF换成ON即可,但 gRPC、c-ares、OpenSSL 也必须同步用静态方式编译。注意静态链接 OpenSSL 和 protobuf 时,你还需要在附加依赖项中增加ws2_32.libcrypt32.lib,否则链接器会报一些奇怪的无法解析的外部符号。

6. 一些心得与后续扩展

折腾完这一整套流程之后,我的整体感受是:在 VS2015 环境下用 gRPC,本质上是用"版本锁定"换"运行稳定"。只要保证 gRPC、protobuf、OpenSSL 三个核心组件的版本彼此匹配,不要随意升级,整个系统就会像老黄牛一样稳。

另外有一个小技巧分享给你:把这些依赖库的文件目录整理成一个固定的C:\grpc-deps以后,可以编写一个.bat脚本一键配置环境变量,包括PROTOBUF_ROOTGRPC_ROOTOPENSSL_ROOT_DIR等。这样每换一台电脑,只需要把grpc-deps文件夹拷贝过去,再运行一次脚本,不需要重新编译任何库。

后续扩展的话,你可以在这个基础上加 TLS 加密传输(gRPC 默认的明文通道在公网环境里是很危险的),也可以配合 HTTP/2 的流式接口做双向通信。如果项目允许,尽量在后续迁移中提升到 VS2017 或 VS2019,那时候 gRPC 的版本选择面就宽多了,很多新特性也能用上。

如果在实操中遇到我上面没列出的报错,欢迎按"编译器版本 + CMake 版本 + gRPC 版本 + 报错代码"的格式整理出来对比排查,这是排查构建类问题最高效的方法。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询