一. grpc简单介绍
在Windows上使用gRPC进行开发主要涉及以下几个步骤:
1. 安装必要的工具和依赖
2. 编写.proto文件定义服务
3. 生成gRPC代码
4. 实现服务器和客户端
5. 编译和运行
下面我会详细解释每个步骤:
1. 安装必要的工具和依赖:
- 安装Visual Studio (推荐2019或更新版本)
- 安装CMake
- 安装vcpkg (C++包管理器)
- 使用vcpkg安装gRPC:
```
vcpkg install grpc:x64-windows
```
2. 编写.proto文件:
创建一个名为 `helloworld.proto` 的文件:
```protobuf
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
```
3. 生成gRPC代码:
使用protoc编译器生成C++代码:
```
protoc -I . --grpc_out=. --plugin=protoc-gen-grpc="path/to/grpc_cpp_plugin.exe" helloworld.proto
protoc -I . --cpp_out=. helloworld.proto
```
这会生成 `helloworld.grpc.pb.h`, `helloworld.grpc.pb.cc`, `helloworld.pb.h` 和 `helloworld.pb.cc` 文件。
4. 实现服务器和客户端:
服务器 (server.cpp):
```cpp
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloRequest;
using helloworld::HelloReply;
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());
return Status::OK;
}
};
void RunServer() {
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();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}
```
客户端 (client.cpp):
```cpp
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloRequest;
using helloworld::HelloReply;
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 {
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
GreeterClient greeter(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
```
5. 编译和运行:
创建一个CMakeLists.txt文件:
```cmake
cmake_minimum_required(VERSION 3.8)
project(grpc_example)
set(CMAKE_CXX_STANDARD 14)
find_package(gRPC CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)
add_executable(server server.cpp helloworld.grpc.pb.cc helloworld.pb.cc)
target_link_libraries(server gRPC::grpc++ protobuf::libprotobuf)
add_executable(client client.cpp helloworld.grpc.pb.cc helloworld.pb.cc)
target_link_libraries(client gRPC::grpc++ protobuf::libprotobuf)
```
然后使用CMake构建项目:
```
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake
cmake --build .
```
运行服务器和客户端:
```
./server
./client
```
这就是在Windows上使用gRPC的基本流程。记住要适当配置项目设置,包括包含路径和库路径。使用vcpkg可以大大简化依赖管理的过程。
如果遇到任何问题,请查看gRPC的官方文档和Windows特定的指南,因为配置可能会根据你的具体环境有所不同。
二. grpc如何维持长连接
使用 gRPC 维持一个长连接主要有两种方式:
1. 使用双向流式 RPC (Bidirectional Streaming RPC)
2. 使用客户端流式 RPC (Client Streaming RPC) 或服务器流式 RPC (Server Streaming RPC)
下面我会详细介绍这两种方法:
1. 双向流式 RPC
这是最灵活的方式,允许客户端和服务器同时发送多个消息。
首先,修改 .proto 文件:
```protobuf
syntax = "proto3";
package longconnection;
service LongConnection {
rpc BidirectionalStream (stream Message) returns (stream Message) {}
}
message Message {
string content = 1;
}
```
服务器端实现:
```cpp
#include <grpcpp/grpcpp.h>
#include "longconnection.grpc.pb.h"
class LongConnectionServiceImpl final : public LongConnection::Service {
public:
Status BidirectionalStream(ServerContext* context,
ServerReaderWriter<Message, Message>* stream) override {
Message message;
while (stream->Read(&message)) {
std::cout << "Received: " << message.content() << std::endl;
// 处理接收到的消息
// ...
// 发送响应
message.set_content("Server response");
stream->Write(message);
}
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
LongConnectionServiceImpl 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();
}
int main() {
RunServer();
return 0;
}
```
客户端实现:
```cpp
#include <grpcpp/grpcpp.h>
#include "longconnection.grpc.pb.h"
class LongConnectionClient {
public:
LongConnectionClient(std::shared_ptr<Channel> channel)
: stub_(LongConnection::NewStub(channel)) {}
void BidirectionalStreamingCall() {
ClientContext context;
std::shared_ptr<ClientReaderWriter<Message, Message>> stream(
stub_->BidirectionalStream(&context));
std::thread writer([stream]() {
std::vector<std::string> messages = {"Hello", "How are you?", "Goodbye"};
for (const std::string& msg : messages) {
Message message;
message.set_content(msg);
stream->Write(message);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
stream->WritesDone();
});
Message server_message;
while (stream->Read(&server_message)) {
std::cout << "Received: " << server_message.content() << std::endl;
}
writer.join();
Status status = stream->Finish();
if (!status.ok()) {
std::cout << "RPC failed" << std::endl;
}
}
private:
std::unique_ptr<LongConnection::Stub> stub_;
};
int main() {
LongConnectionClient client(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
client.BidirectionalStreamingCall();
return 0;
}
```
2. 客户端流式 RPC 或服务器流式 RPC
这两种方法分别适用于客户端需要持续发送数据或服务器需要持续发送数据的场景。
以服务器流式 RPC 为例,修改 .proto 文件:
```protobuf
syntax = "proto3";
package longconnection;
service LongConnection {
rpc ServerStream (StreamRequest) returns (stream StreamResponse) {}
}
message StreamRequest {
string client_id = 1;
}
message StreamResponse {
string message = 1;
}
```
服务器端实现:
```cpp
class LongConnectionServiceImpl final : public LongConnection::Service {
public:
Status ServerStream(ServerContext* context,
const StreamRequest* request,
ServerWriter<StreamResponse>* writer) override {
StreamResponse response;
for (int i = 0; i < 10; i++) {
response.set_message("Message " + std::to_string(i));
writer->Write(response);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return Status::OK;
}
};
```
客户端实现:
```cpp
class LongConnectionClient {
public:
LongConnectionClient(std::shared_ptr<Channel> channel)
: stub_(LongConnection::NewStub(channel)) {}
void ServerStreamingCall() {
ClientContext context;
StreamRequest request;
request.set_client_id("client1");
std::unique_ptr<ClientReader<StreamResponse>> reader(
stub_->ServerStream(&context, request));
StreamResponse response;
while (reader->Read(&response)) {
std::cout << "Received: " << response.message() << std::endl;
}
Status status = reader->Finish();
if (!status.ok()) {
std::cout << "RPC failed" << std::endl;
}
}
private:
std::unique_ptr<LongConnection::Stub> stub_;
};
```
这些方法都可以用来维持长连接。选择哪种方法取决于你的具体需求:
- 如果需要双向通信,使用双向流式 RPC。
- 如果主要是客户端向服务器发送数据,使用客户端流式 RPC。
- 如果主要是服务器向客户端发送数据,使用服务器流式 RPC。
注意,为了保持连接的稳定性,你可能需要实现一些额外的机制,比如:
1. 心跳机制:定期发送小型消息以确保连接仍然活跃。
2. 重连机制:在连接断开时自动重新建立连接。
3. 错误处理:妥善处理网络错误和其他异常情况。
这些机制可以帮助你创建一个更稳定和可靠的长连接。