国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

gRPC結合vcpkg在x64-windows平臺visual studio2019 cmake工程里面的應用

這篇具有很好參考價值的文章主要介紹了gRPC結合vcpkg在x64-windows平臺visual studio2019 cmake工程里面的應用。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

這里我們運用vcpkg去下載安裝gRPC,進入vcpkg目錄后,執(zhí)行命令:.\vcpkg.exe install grpc:x64-windows

grpc在vcpkg里面安裝完成后,我們就來使用grpc做一個簡單的例子。

gRPC顧名思義,就是google的RPC方案,基于protobuf數(shù)據(jù)傳輸,其中proto文件的定義約定了服務器端和客戶端的服務接口協(xié)議。

這里我們就用加法和乘法作為服務器端提供的服務,讓客戶端去調用(RPC),我們建立三個文件夾CPP_Server, CPP_Client, proto 來分別存儲服務器端代碼,客戶端代碼,以及proto文件。項目配置選用cmakelist.txt和cmake來管理。

1.? 服務器端和客戶端的proto定義(calculation.proto文件):

syntax = "proto3";
package data_handler;

service CalculationInterface{
?? ?// Add operation
? ? rpc Add(AddRequest) returns (AddReply){}
?? ?// Multiply operation
? ? rpc Multiply(MultiplyRequest) returns (MultiplyReply){}
}

message AddReply{
? ? int32 result = 1;
}

message AddRequest{
? ? int32 param1 = 1;
? ? int32 param2 = 2;
}

message MultiplyReply{
? ? int32 result = 1;
}

message MultiplyRequest{
? ? int32 param1 = 1;
? ? int32 param2 = 2;
}

?2. 服務器端代碼

在服務器端,我們要在cmakelist里面進行proto文件的解析執(zhí)行成相應的.pb.cc,.pb.h,.grpc.pb.cc,.grpc.pb.h文件,同時對項目文件的配置。

那么我們必然要先找到grpc, protobuf庫和執(zhí)行文件。這時候就需要用到vcpkg這套包管理器,

而下面這句話就是讓vcpkg的包管理起作用的關鍵:

set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")

注意這句話一定要在定義project名字之前,本例子是:project(CalculationInGrpcServer)

這樣子后面的find_package, find_program, target_link_libraries等都會去vckpg里面找到。

cmake_minimum_required(VERSION 3.20)

# Note: 8 target(s) were omitted.


message("--------" $ENV{VCPKG_ROOT})

set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
project(CalculationInGrpcServer)

set(_GRPC_GRPCPP gRPC::grpc++)
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)

set(_PROTOBUF_LIBPROTOBUF_D libprotobufd)
find_package(gRPC CONFIG REQUIRED)
find_program(_PROTOBUF_PROTOC protoc REQUIRED)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)

# Generated proto sources
get_filename_component(proto "../proto/calculation.proto" ABSOLUTE)
get_filename_component(proto_name "../proto/calculation.proto" NAME_WE)
get_filename_component(proto_path "${proto}" PATH)

set(proto_srcs "${proto_path}/${proto_name}.pb.cc")
set(proto_hdrs "${proto_path}/${proto_name}.pb.h")
set(grpc_srcs "${proto_path}/${proto_name}.grpc.pb.cc")
set(grpc_hdrs "${proto_path}/${proto_name}.grpc.pb.h")
message("------------------------------------------------")
message(${_PROTOBUF_PROTOC})
message(${_GRPC_CPP_PLUGIN_EXECUTABLE})
message(${proto_path})

message("-------------------------------------------------")
add_custom_command(
? ? ? OUTPUT "${proto_srcs}" "${proto_hdrs}" "${grpc_srcs}" "${grpc_hdrs}"
? ? ? COMMAND ${_PROTOBUF_PROTOC}
? ? ? ARGS --grpc_out "${proto_path}"
? ? ? ? --cpp_out "${proto_path}"
? ? ? ? -I "${proto_path}"
? ? ? ? --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
? ? ? ? "${proto}"
? ? ? DEPENDS "${proto}")

# Include generated *.pb.h files
include_directories(
? ? "${proto_path}"
? ? )

file(GLOB PUBLIC_HEADER ${CMAKE_CURRENT_BINARY_DIR}/*.h
? ? ? ? ? ? ? ? ? ? ? ? ${PROJECT_SOURCE_DIR}/../proto/*.h)


add_executable(${PROJECT_NAME} CalculationServer.cc ${proto_srcs} ${grpc_srcs})
target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::gpr gRPC::upb gRPC::grpc gRPC::grpc++)

message("protobuf libs are:")
message(${_PROTOBUF_LIBPROTOBUF_D})

在我們的服務器端代碼里面著重用到的是::data_handler::CalculationInterface::Service,這個是proto解釋器幫我們對proto文件解析成cc文件后,里面的一個Service接口,我們代碼里面最主要是去實現(xiàn)這個接口,來看看吧:


#include <grpcpp/grpcpp.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include <calculation.grpc.pb.h>
#include <calculation.pb.h>
#include <thread>

using grpc::Server;
using grpc::ServerBuilder;
using ::grpc::ServerContext;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::Status;

class CalculationInGrpcServerImpl final
    : public ::data_handler::CalculationInterface::Service {
public:
  virtual ~CalculationInGrpcServerImpl(){};

  // Add operation
  ::grpc::Status Add(::grpc::ServerContext* context,
                             const ::data_handler::AddRequest* request,
                             ::data_handler::AddReply* response) override;
  // Multiply operation
  ::grpc::Status Multiply(
      ::grpc::ServerContext* context,
      const ::data_handler::MultiplyRequest* request,
      ::data_handler::MultiplyReply* response) override;
};

::grpc::Status CalculationInGrpcServerImpl::Add(
    ::grpc::ServerContext* context,
    const ::data_handler::AddRequest* request,
    ::data_handler::AddReply* response) {
  if (!context || !request || !response) {
    return ::grpc::Status::CANCELLED;
  }
  int32_t a = request->param1();
  int32_t b = request->param2();
  int32_t result = a + b;
  response->set_result(result);
  std::cout << "Add operation: " << a << " + " << b << std::endl;
  std::cout << "The result is: " << result << std::endl;
  return ::grpc::Status::OK;
}

::grpc::Status CalculationInGrpcServerImpl::Multiply(
    ::grpc::ServerContext* context,
    const ::data_handler::MultiplyRequest* request,
    ::data_handler::MultiplyReply* response) {
  if (!context || !request || !response) {
    return ::grpc::Status::CANCELLED;
  }
  int32_t a = request->param1();
  int32_t b = request->param2();
  int32_t result = a * b;
  response->set_result(result);
  std::cout << "Multiply operation: " << a << " * " << b << std::endl;
  std::cout << "The result is: " << result << std::endl;
  return ::grpc::Status::OK;
}

// define the gRPC server
std::unique_ptr<Server> server_ptr;
CalculationInGrpcServerImpl service;

void RunServer(const std::string& server_address) {
  ServerBuilder builder;
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  builder.RegisterService(&service);

  server_ptr = builder.BuildAndStart();
  std::cout << "Server(" << server_address << ") is listening on ..." << std::endl;
  std::cout << "Press 'q' to exit the server" << std::endl;

  server_ptr->Wait();
}

int main() { 

    std::string server_address("0.0.0.0:50051");
    std::thread server_thread(RunServer, server_address);

    bool running = true;
    while (running) {
      char c = getchar();
      if (c == '\n' || c == EOF) continue;
      if (c == 'q') {
        // reset running flag and shutdown server
        running = false;
        server_ptr->Shutdown();
      }
    }
    server_thread.join();
    return 0; 


}

大家有可能看到了main函數(shù),本人偷懶,將其一起寫在一個文件里了,最好還是將main函數(shù)實現(xiàn)放到另外的文件。當然我們重點是將grpc的運用,大家可以借鑒一下里面server是怎樣綁定IP和port口,運行起來server的。

生成一下,看看是不是和預想的一樣啊?

3. 客戶端代碼

客戶端代碼主要是調用服務器端的接口,就是上面寫的接口,grpc通過一個stub代理來實現(xiàn),這樣我們就象調用本地的函數(shù)一樣去遠程調用函數(shù)接口了,從而達到訪問服務的目的。

客戶端的cmakelist.txt和服務器端的有點類似,我貼出來,大家看看就行:

cmake_minimum_required(VERSION 3.20)

# Note: 8 target(s) were omitted.


message("--------" $ENV{VCPKG_ROOT})

set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
project(CalculationInGrpcClient)

set(_GRPC_GRPCPP gRPC::grpc++)
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)

set(_PROTOBUF_LIBPROTOBUF_D libprotobufd)
find_package(gRPC CONFIG REQUIRED)
find_program(_PROTOBUF_PROTOC protoc REQUIRED)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)

# Generated proto sources
get_filename_component(proto "../proto/calculation.proto" ABSOLUTE)
get_filename_component(proto_name "../proto/calculation.proto" NAME_WE)
get_filename_component(proto_path "${proto}" PATH)

set(proto_srcs "${proto_path}/${proto_name}.pb.cc")
set(proto_hdrs "${proto_path}/${proto_name}.pb.h")
set(grpc_srcs "${proto_path}/${proto_name}.grpc.pb.cc")
set(grpc_hdrs "${proto_path}/${proto_name}.grpc.pb.h")
message("------------------------------------------------")
message(${_PROTOBUF_PROTOC})
message(${_GRPC_CPP_PLUGIN_EXECUTABLE})
message(${proto_path})

message("-------------------------------------------------")
add_custom_command(
? ? ? OUTPUT "${proto_srcs}" "${proto_hdrs}" "${grpc_srcs}" "${grpc_hdrs}"
? ? ? COMMAND ${_PROTOBUF_PROTOC}
? ? ? ARGS --grpc_out "${proto_path}"
? ? ? ? --cpp_out "${proto_path}"
? ? ? ? -I "${proto_path}"
? ? ? ? --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
? ? ? ? "${proto}"
? ? ? DEPENDS "${proto}")

# Include generated *.pb.h files
include_directories(
? ? "${proto_path}"
? ? )

file(GLOB PUBLIC_HEADER ${CMAKE_CURRENT_BINARY_DIR}/*.h
? ? ? ? ? ? ? ? ? ? ? ? ${PROJECT_SOURCE_DIR}/../proto/*.h)

add_executable(${PROJECT_NAME} CalculationClient.cc ${proto_srcs} ${grpc_srcs})
target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::gpr gRPC::upb gRPC::grpc gRPC::grpc++)

message("protobuf libs are:")
message(${_PROTOBUF_LIBPROTOBUF_D})

?下面就是要介紹客戶端的代碼模塊了,我這邊簡單封裝了一個客戶端類去調用服務,代碼如下,

大家看看簡單的request/reply調用方式。


#include <grpcpp/grpcpp.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include <calculation.grpc.pb.h>
#include <calculation.pb.h>
#include <thread>

using grpc::Server;
using grpc::ServerBuilder;
using ::grpc::ServerContext;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::Status;

class CalculationInGrpcClient final {
public:
    CalculationInGrpcClient(CalculationInGrpcClient& param) = delete;
    CalculationInGrpcClient& operator=(CalculationInGrpcClient& param) = delete;

    CalculationInGrpcClient(std::shared_ptr<grpc::Channel> channelPtr);
    ~CalculationInGrpcClient(){};

    bool RequestAddOperation(const int32_t a, const int32_t b, int32_t& result);
    bool RequestMultiplyOperation(const int32_t a, const int32_t b, int32_t& result);

private:

    std::unique_ptr<data_handler::CalculationInterface::Stub> mStub;
};

CalculationInGrpcClient::CalculationInGrpcClient(
    std::shared_ptr<grpc::Channel> channel)
    : mStub(data_handler::CalculationInterface::NewStub(channel)) {}

bool CalculationInGrpcClient::RequestAddOperation(int32_t a, int32_t b,
                                                  int32_t& result) {
    grpc::Status grcpStatus;
    grpc::ClientContext context;
    data_handler::AddReply reply;
    data_handler::AddRequest request;
    request.set_param1(a);
    request.set_param2(b);
    grcpStatus = mStub->Add(&context, request, &reply);

    if (grcpStatus.error_code() == ::grpc::StatusCode::OK) {
        result = static_cast<int32_t>(reply.result());
        std::cout << "After adding operation, the result is: " 
                    << result
                    << std::endl;
        return true;
    } else {
        std::cout << "Server not running..." << std::endl;
    }
    return false;
}

bool CalculationInGrpcClient::RequestMultiplyOperation(int32_t a, int32_t b,
                                                       int32_t& result) {
    grpc::Status grcpStatus;
    grpc::ClientContext context;
    data_handler::MultiplyReply reply;
    data_handler::MultiplyRequest request;
    request.set_param1(a);
    request.set_param2(b);
    grcpStatus = mStub->Multiply(&context, request, &reply);

    if (grcpStatus.error_code() == ::grpc::StatusCode::OK) {
        result = static_cast<int32_t>(reply.result());
        std::cout << "After Multiplication operation, the result is: " 
                    << result
                    << std::endl;
        return true;
    } else {
        std::cout << "Server not running..." << std::endl;
    }
    return false;
}

void showHelp() {
    std::cout << "Calculation starts : \r\n\
            Press 'q' to exit the calculator. \r\n\
            "
            << std::endl;
}

bool FindParamters(const std::string& src, const char operation, int32_t& left, int32_t& right) {
  auto it = src.find(operation);
  if (it != std::string::npos) {
    std::string leftParam = src.substr(0, it);
    std::string rightParam = src.substr(it + 1, src.length() - it - 1);
    left = atoi(leftParam.c_str());
    right = atoi(rightParam.c_str());
    return true;
  }
  return false;
}

int main() { 

    showHelp();
    auto grpcChannel = grpc::CreateChannel("127.0.0.1:50051",
                                         grpc::InsecureChannelCredentials());
    if (!grpcChannel) {
        printf("Failed to create gRPC channel\n");
        return 0;
    }

    std::unique_ptr<CalculationInGrpcClient> clientPtr =
        std::make_unique<CalculationInGrpcClient>(grpcChannel);

    bool running = true;
    while (running) {
        std::string strTmp;
        std::getline(std::cin, strTmp);
        int32_t a = 0;
        int32_t b = 0;
        int32_t result = 0;
        if (FindParamters(strTmp, '+', a, b)) {
            if (clientPtr) {
            clientPtr->RequestAddOperation(a, b, result);
            }
        } else if (FindParamters(strTmp, '*', a, b)) {
            if (clientPtr) {
            clientPtr->RequestMultiplyOperation(a, b, result);
            }
        } else {
            // reserve
        }

        if (strTmp.find('q') != std::string::npos) {
            // reset running flag and shutdown server
            running = false;
        }
    }

    return 0; 


}

代碼里面的request, reply基本上是固定格式:

? ? grpc::Status grcpStatus;
? ? grpc::ClientContext context;
? ? data_handler::MultiplyReply reply;
? ? data_handler::MultiplyRequest request;
? ? request.set_param1(a);
? ? request.set_param2(b);
? ? grcpStatus = mStub->Multiply(&context, request, &reply);

主要是stub去調用服務器端的接口,而前的context, request, reply都是準備工作。

grpc的createChannel綁定了服務器端的IP和port,進行服務器端和客戶端通信,grpc都封裝好了,固定格式調用就行。

4. 編譯生成后,運行服務器端后,在運行客戶端

gRPC結合vcpkg在x64-windows平臺visual studio2019 cmake工程里面的應用

?好了,就先到這里吧,代碼只是demo,大家看看就行,里面有些不嚴謹?shù)牡胤剑喽鄵鷵?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-433001.html

到了這里,關于gRPC結合vcpkg在x64-windows平臺visual studio2019 cmake工程里面的應用的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Windows - Microsoft Visual C++ 2019 Redistributable Package (x64) is not installed

    Windows - Microsoft Visual C++ 2019 Redistributable Package (x64) is not installed

    Microsoft Visual C++ 2019 Redistributable Package (x64) is not installed 另外的幾種報錯形式: ”由于找不到MSVCR120.dll,無法繼續(xù)執(zhí)行代碼。重新安裝程序可能會解決此問題“ ”由于找不到VCRUNTIME140_1.dll,無法繼續(xù)執(zhí)行代碼。重新安裝程序可能會解決此問題“ 都是由于電腦系統(tǒng)缺少部分配置

    2024年02月13日
    瀏覽(25)
  • windows下安裝Visual Studio + CMake+OpenCV + OpenCV contrib+TensorRT

    windows下安裝Visual Studio + CMake+OpenCV + OpenCV contrib+TensorRT

    目錄 1 安裝visual studio 2 安裝CMake 3 OpenCV源碼安裝 3.1 OpenCV源碼下載 3.2 OpenCV contrib源碼下載 3.3 安裝OpenCV 3.4 安裝OpenCV-crontrib 3.5?VS生成代碼 4 環(huán)境配置 5 TensorRT安裝 5.1?TensorRT安裝 5.2 Python下安裝TensorRT庫 最近在研究windows系統(tǒng)上部署安裝目標檢測算法,需要用到OpenCV軟件,因為

    2024年02月09日
    瀏覽(19)
  • Window中,Visual Studio 2022(C++)環(huán)境下安裝OpenCV教程(不用Cmake版本)

    Window中,Visual Studio 2022(C++)環(huán)境下安裝OpenCV教程(不用Cmake版本)

    本教程主要為了方便小白安裝C++版本的OpenCV。 1. 第一步:下載官方OpenCV 下載后,在本地安裝即可,注意記住安裝路徑,后續(xù)需要! 2. 配置系統(tǒng)環(huán)境變量,Path中,新增變量。即opencv安裝的路徑,選到opencv中build/x64/vc15/bin 3. 安裝visual studio 2022,官網(wǎng) 直接,按照C++配置安裝即可

    2024年02月11日
    瀏覽(33)
  • Colmap編譯教程及筆記 [Windows+VS2019+Vcpkg]

    Colmap編譯教程及筆記 [Windows+VS2019+Vcpkg]

    Windows系統(tǒng)下COLMAP的編譯可以分為三部分:(1)手動下載安裝Boost、Qt、CGAL、CUDA(Boost和CGAL也可以用vcpkg安裝);(2)利用vcpkg安裝glew、freeimage、ceres等庫(3)最后在colmap源代碼目錄下的build文件夾打開cmd,輸入cmake命令生成解決方案 CMake、Git、Visual Studio 2019、Vcpkg等提前安裝

    2023年04月08日
    瀏覽(107)
  • CUDA編程第一章:windows下安裝visual studio 2019+CUDA10.2的整體圖文流程

    CUDA編程第一章:windows下安裝visual studio 2019+CUDA10.2的整體圖文流程

    去年雖然看了CUDA編程的基礎知識(沒學完つ﹏?),但是沒有整理成筆記,并且一直沒有使用,導致忘了好多。今年打算重新再把CUDA的基礎知識學習一邊,并進行總結梳理,記錄成文,便于后續(xù)的復習。 本篇博客是CUDA編程系列筆記的開篇,我打算先介紹下搭建CUDA編程環(huán)境

    2024年02月15日
    瀏覽(28)
  • 下載最新版 VC_redist.x86.exe / VC_redist.x64.exe for Visual Studio 2015, 2017, 2019, and 2022

    目錄 1、如何處理程序需要的C/C++運行時庫? 2、VC_redist.x64.exe的主界面以及支持的命令行參數(shù)

    2024年02月11日
    瀏覽(92)
  • 如何在Visual Studio、Clion、Msys2中安裝和使用vcpkg

    如何在Visual Studio、Clion、Msys2中安裝和使用vcpkg

    首先事情是在安裝了Msys2之后,想在Clion中使用安裝在Msys2中的vcpkg。但是折騰了很久還是無法解決。于是就折騰出了這篇文章,和下一篇如何在Clion使用vcpkg的文章。 不過,由于我電腦上已近配置好了vcpkg以及環(huán)境變量,要是重新刪除再搞特別麻煩,于是教程我使用msys2來掩飾

    2023年04月26日
    瀏覽(69)
  • Visual Studio 2022 MASM x64匯編hello world以及調試(Console版 + Windows版)

    本文介紹使用Visual Studio 2022的MASM開發(fā)x64匯編程序hello world的環(huán)境配置和匯編代碼,作為學習CPU指令的起點。分兩個版本的hello world, 一個是console版本,另一個是windows版本。 首先安裝visual studio community 2022,下載地址 https://visualstudio.microsoft.com/,安裝時選擇C++開發(fā)模塊 安裝好以

    2024年02月05日
    瀏覽(31)
  • Windows平臺visual studio與樹莓派跨平臺交叉編譯opencv

    Windows平臺visual studio與樹莓派跨平臺交叉編譯opencv

    由于架構差異,opencv在Windows平臺下編譯的程序無法直接移植到樹莓派環(huán)境使用,所以需要進行Cmake和跨平臺的交叉編譯。網(wǎng)上包括官方文檔很多足夠參考,但配置過程中依然會出現(xiàn)個性問題,以下是本人基于windows下使用visual studio2017集成開發(fā)環(huán)境進行交叉編譯環(huán)境配置過程中

    2024年04月11日
    瀏覽(30)
  • visual Studio MFC 平臺實現(xiàn)拉普拉斯和拉普拉斯與直方圖均衡化與中值濾波相結合實現(xiàn)比較

    visual Studio MFC 平臺實現(xiàn)拉普拉斯和拉普拉斯與直方圖均衡化與中值濾波相結合實現(xiàn)比較

    本文使用visual Studio MFC 平臺實現(xiàn)圖像增強中的拉普拉斯變換,同時拉普拉斯一般不會單獨使用,與其他平滑操作相結合,本文使用了拉普拉斯與直方圖均衡化以及與中值濾波相結合,也對三種方式進行了對比 關于基礎工程的創(chuàng)建可以參考 01-Visual Studio 使用MFC 單文檔工程繪制

    2024年02月04日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包