这个例子向用户展示了如何使用gRPC C++客户端获取Trojan服务器的流量和速度信息,并将其封装为一个动态库供外部使用。首先,我们演示了如何创建一个基本的gRPC C++客户端。然后,我们将其扩展为一个动态库,使外部项目可以轻松地使用该库来获取流量和速度数据,而无需直接依赖于gRPC库。我们还提供了构建动态库和外部项目的CMakeLists.txt文件,以及如何使用CMake构建和运行这些项目。
使用 gRPC C++客户端获取流量
要使用gRPC C++客户端获取流量,请按照以下步骤进行操作:
- 首先确保安装了gRPC和protobuf。请参考gRPC C++快速入门文档(https://grpc.io/docs/languages/cpp/quickstart/ )了解如何安装gRPC和protobuf。
- 使用
protoc
命令行工具将上述的.proto
文件编译为C++源文件。运行以下命令:
# 生成C++代码
protoc -I. --cpp_out=. trojan.proto
# 生成C++的gRPC代码
protoc -I. --grpc_out=. --plugin=protoc-gen-grpc="C:\dev\vcpkg\installed\x64-windows\tools\grpc\grpc_cpp_plugin.exe" trojan.proto
protoc(Protocol Buffers)编译器参数说明。
-I
:指定搜索.proto文件的目录,这里是当前目录(.)。
--cpp_out
:指示编译器生成C++代码,输出到当前目录。
--grpc_out
:指示编译器生成C++的gRPC代码,输出到当前目录。
--plugin
指定用于生成gRPC代码的插件。这里使用 protoc-gen-grpc 指定插件具体位置。
这将生成以下文件:
- trojan.pb.h
- trojan.pb.cc
- trojan.grpc.pb.h
- trojan.grpc.pb.cc
这四个文件是由protoc编译器根据您的.proto文件生成的C++代码。它们包含了Protocol Buffers和gRPC相关的代码,用于构建客户端和服务端。以下是每个文件的主要用途:
-
trojan.pb.h:这是Protocol Buffers生成的头文件,包含了.proto文件中定义的所有消息和枚举类型的C++类声明。
-
trojan.pb.cc:这是Protocol Buffers生成的源文件,包含了trojan.pb.h中声明的C++类的实现。这些类用于序列化和反序列化消息。
-
trojan.grpc.pb.h:这是gRPC生成的头文件,包含了.proto文件中定义的所有服务接口的C++类声明。为实现服务端,您需要继承这些类并实现具体的RPC方法。为实现客户端,您将使用这些类生成的Stub。
-
trojan.grpc.pb.cc:这是gRPC生成的源文件,包含了trojan.grpc.pb.h中声明的C++类的实现。这些类主要包括gRPC客户端Stub和服务端Skeleton。
-
在C++项目中包含生成的头文件,然后创建一个gRPC客户端。以下是一个简单的示例:
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "trojan.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using trojan::api::TrojanClientService;
using trojan::api::GetTrafficRequest;
using trojan::api::GetTrafficResponse;
class TrojanClient {
public:
TrojanClient(std::shared_ptr<Channel> channel)
: stub_(TrojanClientService::NewStub(channel)) {}
bool GetTraffic(const std::string& password) {
GetTrafficRequest request;
request.mutable_user()->set_password(password);
GetTrafficResponse response;
ClientContext context;
Status status = stub_->GetTraffic(&context, request, &response);
if (status.ok()) {
std::cout << "Traffic: Upload " << response.traffic_total().upload_traffic()
<< " bytes, Download " << response.traffic_total().download_traffic() << " bytes."
<< std::endl;
return true;
} else {
std::cout << "Error: " << status.error_code() << ": " << status.error_message()
<< std::endl;
return false;
}
}
private:
std::unique_ptr<TrojanClientService::Stub> stub_;
};
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " <password>" << std::endl;
return 1;
}
std::string password(argv[1]);
auto channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
TrojanClient client(channel);
if (client.GetTraffic(password)) {
std::cout << "Traffic retrieved successfully." << std::endl;
} else {
std::cout << "Failed to retrieve traffic." << std::endl;
}
return 0;
}
-
编译并运行客户端。将main.cpp(或其他包含主函数的源文件)与生成的源文件一起编译,以创建可执行文件。注意,需要链接gRPC和protobuf库。
-
运行客户端。确保Trojan服务器正在运行并监听指定的端口(例如localhost:50051),然后运行客户端可执行文件以获取流量。
将获取流量和速度的功能封装在一个动态库中供外部使用
为了将获取流量和速度的功能封装在一个动态库中供外部使用,您需要修改C++代码,创建一个新的类并将相关功能导出为动态库。以下是一个示例:
- 创建一个名为
trojan_client.h
的头文件,用于定义我们将导出的类和方法:
#ifndef TROJAN_CLIENT_H
#define TROJAN_CLIENT_H
#include <cstdint>
#include <string>
namespace trojan_client {
struct Traffic {
uint64_t upload_traffic;
uint64_t download_traffic;
};
struct Speed {
uint64_t upload_speed;
uint64_t download_speed;
};
/*!
socket_address:localhost:50051
*/
extern bool GetTrafficAndSpeed(const std::string socket_address/*ip:port*/, const std::string& password, Traffic& traffic, Speed& speed);
} // namespace trojan_client
#endif // TROJAN_CLIENT_H
- 修改之前的
main.cpp
,将其重命名为trojan_client.cpp
,并根据新的需求对其进行修改:
#include "trojan_client.h"
#include "trojan.grpc.pb.h"
#include <grpcpp/grpcpp.h>
#include <memory>
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using trojan::api::TrojanClientService;
using trojan::api::GetTrafficRequest;
using trojan::api::GetTrafficResponse;
namespace trojan_client {
class TrojanClient {
public:
TrojanClient(std::shared_ptr<Channel> channel)
: stub_(TrojanClientService::NewStub(channel)) {}
bool GetTrafficAndSpeed(const std::string& password, Traffic& traffic, Speed& speed) {
GetTrafficRequest request;
request.mutable_user()->set_password(password);
GetTrafficResponse response;
ClientContext context;
Status status = stub_->GetTraffic(&context, request, &response);
if (status.ok()) {
traffic.upload_traffic = response.traffic_total().upload_traffic();
traffic.download_traffic = response.traffic_total().download_traffic();
speed.upload_speed = response.speed_current().upload_speed();
speed.download_speed = response.speed_current().download_speed();
return true;
} else {
return false;
}
}
private:
std::unique_ptr<TrojanClientService::Stub> stub_;
};
bool GetTrafficAndSpeed(const std::string socket_address, const std::string& password, Traffic& traffic, Speed& speed) {
auto channel = grpc::CreateChannel(socket_address, grpc::InsecureChannelCredentials());
TrojanClient client(channel);
return client.GetTrafficAndSpeed(password, traffic, speed);
}
} // namespace trojan_client
- 在CMakeLists.txt中添加如下代码,以创建动态库:
cmake_minimum_required(VERSION 3.5)
project(trojan_client_lib)
find_package(gRPC CONFIG REQUIRED)
add_library(trojan_client SHARED
trojan_client.cpp
trojan.pb.cc
trojan.grpc.pb.cc
)
target_include_directories(trojan_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(trojan_client
gRPC::grpc++
gRPC::grpc
protobuf::libprotobuf
)
-
使用CMake构建项目,以生成动态库(例如,
libtrojan_client.so
、libtrojan_client.dylib
或trojan_client.dll
,具体取决于您的平台)。 -
编写一个新的C++源文件,例如
main.cpp
,以使用动态库:
#include <iostream>
#include "trojan_client.h"
#include "trojan.grpc.pb.h"
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " <password>" << std::endl;
return 1;
}
std::string password(argv[1]);
TrojanClient client("localhost:50051");
trojan::api::Traffic traffic;
trojan::api::Speed speed;
if (client.GetTrafficAndSpeed(“localhost:50051”,password, &traffic, &speed)) {
std::cout << "Traffic: Upload " << traffic.upload_traffic()
<< " bytes, Download " << traffic.download_traffic() << " bytes."
<< std::endl;
std::cout << "Speed: Upload " << speed.upload_speed()
<< " bps, Download " << speed.download_speed() << " bps."
<< std::endl;
} else {
std::cout << "Failed to retrieve traffic and speed." << std::endl;
}
return 0;
}
- 编写一个新的CMakeLists.txt文件,以链接动态库并构建可执行文件:
cmake_minimum_required(VERSION 3.5)
project(trojan_client_app)
find_package(gRPC CONFIG REQUIRED)
add_executable(trojan_client_app
main.cpp
)
# Replace the path below with the path to the directory containing the trojan_client library
set(TROJAN_CLIENT_LIB_DIR /path/to/trojan_client_lib)
target_include_directories(trojan_client_app PRIVATE ${TROJAN_CLIENT_LIB_DIR})
target_link_directories(trojan_client_app PRIVATE ${TROJAN_CLIENT_LIB_DIR})
target_link_libraries(trojan_client_app
trojan_client
)
-
使用CMake构建项目以生成可执行文件。
-
确保在运行可执行文件时,动态库(如
libtrojan_client.so
、libtrojan_client.dylib
或trojan_client.dll
)可以在您的系统的库搜索路径中找到。您可以将其复制到系统库目录中,或通过设置LD_LIBRARY_PATH
(Linux/macOS)或PATH
(Windows)环境变量来指定库的位置。
现在,您已经创建了一个动态库,它封装了获取流量和速度的功能,并可以在外部程序中使用。