使用 Qt GRPC 构建高效的 Trojan-Go 客户端:详细指南
- 使用 Qt GRPC 构建高效的 Trojan-Go 客户端:详细指南
- 初识 Qt 和 gRPC
- 什么是 Qt?
- 什么是 gRPC?
- 项目结构概述
- 创建 proto 文件定义 API
- 下载
api.proto
文件 - 解析 proto 文件
- 1.
package
与option
语句 - 2. 消息类型定义
Traffic
Speed
User
UserStatus
GetTrafficRequest
和GetTrafficResponse
ListUsersRequest
和ListUsersResponse
GetUsersRequest
和GetUsersResponse
SetUsersRequest
和SetUsersResponse
- 3. 服务定义
TrojanClientService
TrojanServerService
- 1.
- 下载
- 配置 CMake 构建系统
- CMake 配置解析
- 实现 Trojan-Go 客户端
main.cpp
内容- 代码详解
- 使用定时器实现数据流量和速度监控
- 总结
- 初识 Qt 和 gRPC
使用 Qt GRPC 构建高效的 Trojan-Go 客户端:详细指南
在上一篇 使用gRPC C++创建动态库以获取Trojan-go客户端的流量和速度信息 文章中使用纯 C++,这次随着 qt6.8 lts 发布,Qt GRPC 作为稳定模块推出,借此机会使用 Qt GRPC 实现该功能。
初识 Qt 和 gRPC
什么是 Qt?
Qt 是一个跨平台的 C++ 框架,用于开发图形界面和非图形界面应用。它提供了丰富的库支持、良好的跨平台兼容性、自动化的信号槽机制,使得在大型应用程序中管理事件和响应更为便捷。在本文中,Qt 提供了自动化工具来简化 Protobuf 和 gRPC 的代码生成,并结合 Qt 的 gRPC 模块,使得构建 gRPC 客户端更加便捷。
什么是 gRPC?
gRPC 是一种高性能的 RPC 框架,由 Google 开发,用于实现不同语言之间的服务调用。其基于 HTTP/2 协议,利用 Protocol Buffers(Protobuf)作为接口描述语言来定义服务和数据结构,允许客户端和服务器之间的轻量化通信。gRPC 的优势在于它的跨平台支持、灵活性和良好的性能,广泛应用于微服务架构和物联网领域。
项目结构概述
在创建项目之前,我们先来定义项目的目录结构,以确保文件结构清晰、便于管理:
trojan-go
├── api.proto # Protobuf 文件,用于定义 gRPC 服务和消息格式
├── CMakeLists.txt # CMake 构建文件,配置了编译选项、依赖项
└── main.cpp # 主程序文件,实现客户端功能
- api.proto:包含 API 的 gRPC 服务定义和数据结构。
- CMakeLists.txt:配置 CMake 构建系统。
- main.cpp:实现了 Trojan-Go 客户端的主要逻辑。
强制依赖
protoc.exe
,需要添加到环境变量中,从https://github.com/protocolbuffers/protobuf/releases/download/v28.3/protoc-28.3-win64.zip
下载。
创建 proto 文件定义 API
proto 文件定义了客户端与服务器之间的通信规则。我们将在 api.proto
文件中定义数据结构和 gRPC 服务接口,确保客户端和服务器之间能够准确传递数据。
下载 api.proto
文件
下载 trojan-go api.proto 文件,文件内容如下:
syntax = "proto3";
package trojan.api;
option go_package = "github.com/p4gefau1t/trojan-go/api/service";
message Traffic {
uint64 upload_traffic = 1;
uint64 download_traffic = 2;
}
message Speed {
uint64 upload_speed = 1;
uint64 download_speed = 2;
}
message User {
string password = 1;
string hash = 2;
}
message UserStatus {
User user = 1;
Traffic traffic_total = 2;
Speed speed_current = 3;
Speed speed_limit = 4;
int32 ip_current = 5;
int32 ip_limit = 6;
}
message GetTrafficRequest {
User user = 1;
}
message GetTrafficResponse {
bool success = 1;
string info = 2;
Traffic traffic_total = 3;
Speed speed_current = 4;
}
message ListUsersRequest {
}
message ListUsersResponse {
UserStatus status = 1;
}
message GetUsersRequest {
User user = 1;
}
message GetUsersResponse {
bool success = 1;
string info = 2;
UserStatus status = 3;
}
message SetUsersRequest {
enum Operation {
Add = 0;
Delete = 1;
Modify = 2;
}
UserStatus status = 1;
Operation operation = 2;
}
message SetUsersResponse {
bool success = 1;
string info = 2;
}
service TrojanClientService {
rpc GetTraffic(GetTrafficRequest) returns(GetTrafficResponse){}
}
service TrojanServerService {
// list all users
rpc ListUsers(ListUsersRequest) returns(stream ListUsersResponse){}
// obtain specified user's info
rpc GetUsers(stream GetUsersRequest) returns(stream GetUsersResponse){}
// setup existing users' config
rpc SetUsers(stream SetUsersRequest) returns(stream SetUsersResponse){}
}
解析 proto 文件
这个 api.proto
文件定义了用于 Trojan API 的 gRPC 服务和消息结构,描述了客户端与服务器之间的通信模式。以下是该文件的详细解释:
1. package
与 option
语句
package trojan.api;
option go_package = "github.com/p4gefau1t/trojan-go/api/service";
package
声明了 proto 文件的包名,trojan.api
用于在生成的代码中作为命名空间。option go_package
定义了用于 Go 语言生成代码的包名,便于 Go 项目中导入。
2. 消息类型定义
Traffic
message Traffic {
uint64 upload_traffic = 1;
uint64 download_traffic = 2;
}
Traffic
消息定义了用户的上传和下载流量,字段类型为uint64
表示无符号 64 位整数,适合表示较大的流量数据。
Speed
message Speed {
uint64 upload_speed = 1;
uint64 download_speed = 2;
}
Speed
消息记录了上传和下载速度,同样使用uint64
类型来表示。
User
message User {
string password = 1;
string hash = 2;
}
User
消息包含用户的密码和哈希值,用于用户身份验证或标识。
UserStatus
message UserStatus {
User user = 1;
Traffic traffic_total = 2;
Speed speed_current = 3;
Speed speed_limit = 4;
int32 ip_current = 5;
int32 ip_limit = 6;
}
UserStatus
消息表示用户的状态信息,包含用户数据 (User
)、总流量 (Traffic
)、当前和限制速度 (Speed
)、当前和限制的 IP 数量。
GetTrafficRequest
和 GetTrafficResponse
message GetTrafficRequest {
User user = 1;
}
message GetTrafficResponse {
bool success = 1;
string info = 2;
Traffic traffic_total = 3;
Speed speed_current = 4;
}
GetTrafficRequest
包含User
数据,用于请求流量信息。GetTrafficResponse
返回请求状态success
、信息info
,以及流量数据和速度数据。
ListUsersRequest
和 ListUsersResponse
message ListUsersRequest {}
message ListUsersResponse {
UserStatus status = 1;
}
ListUsersRequest
不含字段,用于请求用户列表。ListUsersResponse
返回每个用户的状态信息。
GetUsersRequest
和 GetUsersResponse
message GetUsersRequest {
User user = 1;
}
message GetUsersResponse {
bool success = 1;
string info = 2;
UserStatus status = 3;
}
GetUsersRequest
请求特定用户的信息。GetUsersResponse
返回状态success
、信息info
和用户状态UserStatus
。
SetUsersRequest
和 SetUsersResponse
message SetUsersRequest {
enum Operation {
Add = 0;
Delete = 1;
Modify = 2;
}
UserStatus status = 1;
Operation operation = 2;
}
message SetUsersResponse {
bool success = 1;
string info = 2;
}
SetUsersRequest
包含UserStatus
和Operation
,用于增加、删除或修改用户。SetUsersResponse
返回操作状态success
和相关信息info
。
3. 服务定义
TrojanClientService
service TrojanClientService {
rpc GetTraffic(GetTrafficRequest) returns(GetTrafficResponse){}
}
- 定义
GetTraffic
RPC 方法,允许客户端请求其流量信息。
TrojanServerService
service TrojanServerService {
// list all users
rpc ListUsers(ListUsersRequest) returns(stream ListUsersResponse){}
// obtain specified user's info
rpc GetUsers(stream GetUsersRequest) returns(stream GetUsersResponse){}
// setup existing users' config
rpc SetUsers(stream SetUsersRequest) returns(stream SetUsersResponse){}
}
TrojanServerService
包含 3 个方法:ListUsers
:返回用户列表,使用数据流式响应。GetUsers
:获取特定用户信息,支持流式请求和响应。SetUsers
:修改用户信息,通过流式请求和响应实现。
这个 api.proto
文件定义了 Trojan API 的服务与数据结构,支持流量查询、用户信息获取、用户列表和配置管理等功能。
配置 CMake 构建系统
接下来在 CMakeLists.txt
文件中配置 CMake,以便编译和链接所需的库和依赖。
cmake_minimum_required(VERSION 3.16)
project(trojan-go LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Protobuf Grpc)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Protobuf Grpc)
add_executable(client main.cpp)
qt_add_protobuf(client
PROTO_FILES
api.proto
)
qt_add_grpc(client CLIENT
PROTO_FILES
api.proto
)
include(GNUInstallDirs)
install(TARGETS client
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
CMake 配置解析
qt_add_protobuf
和qt_add_grpc
用于生成 Protobuf 和 gRPC 的 C++ 代码。install
指定安装路径,将可执行文件安装到系统的标准目录。
实现 Trojan-Go 客户端
main.cpp
文件实现了客户端的主要逻辑,负责与服务器通信并处理流量信息。
main.cpp
内容
#include <QCoreApplication>
#include <QTimer>
#include <memory>
#include "api.qpb.h"
#include "api_client.grpc.qpb.h"
#include <QGrpcHttp2Channel>
#include <QGrpcChannelOptions>
#include <QGrpcStatus>
#include <QGrpcCallReply>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
trojan::api::TrojanClientService::Client client;
QGrpcChannelOptions channelOptions;
QUrl clientApi = QUrl("http://127.0.0.128:10000"/*用户设定的 trojan-go api 地址和端口*/, QUrl::StrictMode);
std::shared_ptr<QGrpcHttp2Channel> channel = std::make_shared<QGrpcHttp2Channel>(clientApi, channelOptions);
if(client.attachChannel(channel)) {
qDebug() << "attachChannel success";
} else {
qDebug() << "attachChannel failed";
};
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &a, [&]() {
trojan::api::User user;
user.setPassword("trojan-go.com"/*用户连接服务器的密码*/);
trojan::api::GetTrafficRequest req;
req.setUser(user);
std::unique_ptr<QGrpcCallReply> reply = client.GetTraffic(req);
auto *replyPtr = reply.get();
QObject::connect(
replyPtr,
&QGrpcCallReply::finished,
&a,
[reply = std::move(reply)](const QGrpcStatus &status) {
if(!status.isOk()) {
qDebug() << "Failed to send message: " << status;
} else {
trojan::api::GetTrafficResponse response;
if(reply->read(&response)) {
qDebug() << "Success:" << response.success();
qDebug() << "Info:" << response.info();
double downloadTrafficMB = response.trafficTotal().downloadTraffic() / 1024.0 / 1024.0;
double uploadTrafficMB = response.trafficTotal().uploadTraffic() / 1024.0 / 1024.0;
double downloadSpeedMBps = response.speedCurrent().downloadSpeed() / 1024.0 / 1024.0;
double uploadSpeedMBps = response.speedCurrent().uploadSpeed() / 1024.0 / 1024.0;
qDebug() << QString("Download Traffic: %1 MB").arg(downloadTrafficMB, 0, 'f', 2);
qDebug() << QString("Upload Traffic: %1 MB").arg(uploadTrafficMB, 0, 'f', 2);
qDebug() << QString("Download Speed: %1 MB/s").arg(downloadSpeedMBps, 0, 'f', 2);
qDebug() << QString("Upload Speed: %1 MB/s").arg(uploadSpeedMBps, 0, 'f', 2);
qDebug() << "";
} else {
qDebug() << "Failed to parse GetTrafficResponse.";
}
}
},
Qt::SingleShotConnection);
});
timer.start(500);
return a.exec();
}
代码详解
- 连接服务器:使用
QGrpcHttp2Channel
通过 HTTP/2 连接到服务器。 - 定时器监控:每 500 毫秒发送一次
GetTraffic
请求以获取最新流量和速度统计数据。 - 结果解析和输出:解析服务器返回的流量数据并输出到控制台。
使用定时器实现数据流量和速度监控
通过 Qt 的 QTimer
实现客户端定时请求服务器流量和速度统计数据并进行处理。在实际应用中,定时更新数据可以实时监控服务器的状态,有助于及时获取连接信息并检测异常。
总结
本文介绍了如何使用 Qt 和 gRPC 实现一个高效的 Trojan-Go 客户端。从环境配置、Protobuf 文件定义、CMake 构建配置到主程序逻辑,我们全面讲解了客户端实现的每一步骤。借助 Qt 和 gRPC 的自动化工具,开发者能够更高效地实现客户端应用,同时享受 gRPC 高效的通信和 Qt 的跨平台支持。这一项目展示了 gRPC 的强大应用场景,也为开发更复杂的网络应用打下了基础。