gRPC函数之间远程调用是实现了,但是还是非常的不方便,不够自动化。
1.要写proto文件,应该自动生成
2.还是要封解包参数,每个函数都得做一次。
我想要的是两点之间只用一个接口,传所有函数。
所有函数封装成发送与接收 :函数名,请求参数,响应参数,返回值
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string methodName = 1; //函数名唯一标识
string jsonPara=2; //所有参数封装到json
}
message HelloReply {
string jsonPara=2; //所有参数封装到json
string resultCode=3; //返回值
}
client实现 (C++)
int invole(methodName,...)
{
}
server实现(java)
java反射得到函数名,与传过来的对比
mapping()
{
}
如果是自己用,上面完成可以实现。
最终采用:gRPC+protobuf+json方案
因为需要提供接口给用户,所以把函数名全给出,只是把参数转为json。
proto内容:函数名,请求jsonPara所有参数,响应jsonPara所有参数,返回值。
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.pos.remotemethods";
option java_outer_classname = "PosRemoteMethods";
option objc_class_prefix = "PRM";
package pos;
service RemoteMethods{
rpc LoadSymKey(Request) returns (Reply) {}
rpc GetDeviceInfo(Request) returns (Reply) {}
}
message Request {
string jsonPara=1;
}
message Reply {
string jsonPara=1;
uint32 resultCode=2;
}
约定:
1.byte数组 都转成十六进制字符串,收发都转。好处理些。但有些长度是指针的,所有长度直接用整型传值。
2.结构体像单独字段一样传。一般不会与其它参数同名。
Server
#include <iostream>
#include <memory>
#include <string>
#include <json.h>
using namespace Json;
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>
#ifdef BAZEL_BUILD
#include "examples/protos/pos.grpc.pb.h"
#else
#include "pos.grpc.pb.h"
#endif
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using pos::RemoteMethods;
using pos::Reply;
using pos::Request;
using namespace std;
//将字符串编码成16进制数字,适用于所有字符(包括中文)
std::string encodeHexString(const std::string& str) {
// 根据默认编码获取字节数组
const std::string hexString = "0123456789abcdef";
string sb;
// 将字节数组中每个字节拆解成2位16进制整数
for (int i = 0; i < str.length(); i++) {
sb += hexString.at((str[i] & 0xf0) >> 4);
sb += hexString.at((str[i] & 0x0f) >> 0);
}
return sb;
}
//16进制字符串转字符串
std::string hexStringToString(const std::string& hexStr)
{
std::string ret;
const std::string hexString = "0123456789abcdef";
// 将每2位16进制整数组装成一个字节
for (int i = 0; i < hexStr.length(); i += 2)
ret += BYTE(hexString.find(hexStr.at(i)) << 4 | hexString.find(hexStr.at(i + 1)));
return ret;
}
//字节数组转16进制字符串
std::string bytesToHexString(const BYTE* bytes, const int length)
{
if (bytes == NULL) {
return "";
}
std::string buff;
const int len = length;
for (int j = 0; j < len; j++) {
int high = bytes[j] / 16, low = bytes[j] % 16;
buff += (high < 10) ? ('0' + high) : ('a' + high - 10);
buff += (low < 10) ? ('0' + low) : ('a' + low - 10);
}
return buff;
}
//16进制字符串 转 字节数组
void hexStringToBytes(const std::string& hex, BYTE* bytes)
{
int bytelen = hex.length() / 2;
std::string strByte;
unsigned int n;
for (int i = 0; i < bytelen; i++)
{
strByte = hex.substr(i * 2, 2);
sscanf_s(strByte.c_str(), "%x", &n);
bytes[i] = n;
}
}
// Logic and data behind the server's behavior.
class RemoteMethodsServiceImpl final : public RemoteMethods::Service {
Status GetDeviceInfo(ServerContext* context, const Request* request,
Reply* reply) override {
std::cout << request->jsonpara() << std::endl;
Json::Reader reader;
Json::Value value;
if (reader.parse(request->jsonpara(), value))
{
std::cout << "bFlag:" << value["bFlag"].asInt() << std::endl;
std::cout << "pdwInfoLen:" << value["pdwInfoLen"].asInt() << std::endl;
}
std::cout << std::endl;
std::cout << std::endl;
Json::Value root;
unsigned char info[] = { 0x44,0x00,0x45 ,0x46 };
root["pbInfo"] = bytesToHexString(info, sizeof(info));
root["pdwInfoLen"] = sizeof(info);
std::string para = root.toStyledString();
std::cout << para << std::endl;
reply->set_jsonpara(para);
reply->set_resultcode(0);
return Status::OK;
}
Status LoadSymKey(ServerContext* context, const Request* request,Reply* reply) override {
//unsigned char out[300] = { 1 }; //测试
//hexStringToBytes("1122003344", out);
//std::cout << bytesToHexString(out, 1) << std::endl;
//std::cout << hexStringToString("323334") << std::endl;
//std::cout << encodeHexString("323334") << std::endl;
std::cout << request->jsonpara() << std::endl;
Json::Reader reader;
Json::Value value;
if (reader.parse(request->jsonpara(), value))
{
std::cout << "bKeyType:" << value["bKeyType"].asInt() << std::endl;
std::cout << "wKeyIndex:" << value["wKeyIndex"].asInt() << std::endl;
std::cout << "bAlg:" << value["bAlg"].asInt() << std::endl;
std::cout << "bProtectKeyType:" << value["bProtectKeyType"].asInt() << std::endl;
std::cout << "wProtectKeyIndex:" << value["wProtectKeyIndex"].asInt() << std::endl;
std::cout << "pbKeyData:" << value["pbKeyData"].asString() << std::endl;
std::cout << "wKeyDataLen:" << value["wKeyDataLen"].asInt() << std::endl;
std::cout << "pbKCV:" << value["pbKCV"].asString() << std::endl;
std::cout << "bKCVLen:" << value["bKCVLen"].asInt() << std::endl;
std::cout << "bKCVMode:" << value["bKCVMode"].asInt() << std::endl;
}
std::cout << std::endl;
std::cout << std::endl;
Json::Value root;
unsigned char kcv[] = { 0x44,0x00,0x45 ,0x46 };
root["pbKCV"] = bytesToHexString(kcv, sizeof(kcv));
std::string para = root.toStyledString();
std::cout << para << std::endl;
reply->set_jsonpara(para);
reply->set_resultcode(0);
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
RemoteMethodsServiceImpl service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}
int testmain(int argc, char** argv) {
RunServer();
return 0;
}
Client
#include <iostream>
#include <memory>
#include <string>
#include <json.h>
using namespace Json;
#define RPC_FAILED 9999
#include <grpcpp/grpcpp.h>
#ifdef BAZEL_BUILD
#include "examples/protos/pos.grpc.pb.h"
#else
#include "pos.grpc.pb.h"
#endif
using namespace std;
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using pos::RemoteMethods;
using pos::Reply;
using pos::Request;
using pos::Reply;
typedef unsigned char BYTE;
typedef unsigned char UCHAR;
typedef unsigned char *PUCHAR;
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef const void *LPCVOID;
typedef long LONG;
typedef const char *LPCSTR;
typedef const BYTE *LPCBYTE;
typedef BYTE *LPBYTE;
typedef DWORD *LPDWORD;
typedef char *LPSTR;
/* these types were deprecated but still used by old drivers and
* applications. So just declare and use them. */
/* types unused by pcsc-lite */
typedef unsigned short WORD;
#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif
extern std::string encodeHexString(const std::string& str);
extern std::string hexStringToString(const std::string& hexStr);
extern std::string bytesToHexString(const BYTE* bytes, const int length);
extern void hexStringToBytes(const std::string& hex, BYTE* bytes);
class RemoteMethodsClient {
public:
RemoteMethodsClient(std::shared_ptr<Channel> channel)
: stub_(RemoteMethods::NewStub(channel)) {}
// Assembles the client's payload, sends it and presents the response back
// from the server.
DWORD LoadSymKey(IN BYTE bKeyType, IN WORD wKeyIndex, IN BYTE bAlg, IN BYTE bProtectKeyType, IN WORD wProtectKeyIndex, IN BYTE *pbKeyData, IN WORD wKeyDataLen,
IN OUT BYTE *pbKCV, IN BYTE bKCVLen, IN BYTE bKCVMode) {
Request request;
Json::Value root;
root["bKeyType"] = bKeyType;
root["wKeyIndex"] = wKeyIndex;
root["bAlg"] = bAlg;
root["bProtectKeyType"] = bProtectKeyType;
root["wProtectKeyIndex"] = wProtectKeyIndex;
root["pbKeyData"] = bytesToHexString(pbKeyData, wKeyDataLen);
root["wKeyDataLen"] = wKeyDataLen;
root["pbKCV"] = bytesToHexString(pbKCV, bKCVLen);
root["bKCVLen"] = bKCVLen;
root["bKCVMode"] = bKCVMode;
std::string para = root.toStyledString();
std::cout << para << std::endl;
request.set_jsonpara(para);
Reply reply;
ClientContext context;
Status status = stub_->LoadSymKey(&context, request, &reply);
if (status.ok()) {
std::cout << "resultcode=" << reply.resultcode() << std::endl;
//std::cout << "jsonpara=" << reply.jsonpara() << std::endl;
Json::Reader reader;
Json::Value value;
if (reader.parse(reply.jsonpara(), value))
{
if (bKCVLen > 0)
{
std::string para = value["pbKCV"].asString();
std::cout << "pbKCV=" << para << std::endl;
hexStringToBytes(para, pbKCV);
}
}
return reply.resultcode();
}
else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return RPC_FAILED;
}
}
DWORD GetDeviceInfo(OUT BYTE *pbInfo, IN OUT DWORD *pdwInfoLen, IN BYTE bFlag) {
Request request;
Json::Value root;
root["pdwInfoLen"] = (unsigned int)*pdwInfoLen;
root["bFlag"] = bFlag;
std::string para = root.toStyledString();
std::cout << para << std::endl;
request.set_jsonpara(para);
Reply reply;
ClientContext context;
Status status = stub_->GetDeviceInfo(&context, request, &reply); //
if (status.ok()) {
std::cout << "resultcode=" << reply.resultcode() << std::endl;
Json::Reader reader;
Json::Value value;
if (reader.parse(reply.jsonpara(), value))
{
hexStringToBytes(value["pbInfo"].asString(), pbInfo);
*pdwInfoLen = value["pdwInfoLen"].asInt();
}
return reply.resultcode();
}
else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return RPC_FAILED;
}
}
private:
std::unique_ptr<RemoteMethods::Stub> stub_;
};
int main(int argc, char** argv) {
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint specified by
// the argument "--target=" which is the only expected argument.
// We indicate that the channel isn't authenticated (use of
// InsecureChannelCredentials()).
std::string target_str;
std::string arg_str("--target");
if (argc > 1) {
std::string arg_val = argv[1];
size_t start_pos = arg_val.find(arg_str);
if (start_pos != std::string::npos) {
start_pos += arg_str.size();
if (arg_val[start_pos] == '=') {
target_str = arg_val.substr(start_pos + 1);
} else {
std::cout << "The only correct argument syntax is --target="
<< std::endl;
return 0;
}
} else {
std::cout << "The only acceptable argument is --target=" << std::endl;
return 0;
}
} else {
target_str = "localhost:50051";
}
RemoteMethodsClient remoteMethods(
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
unsigned char keyData[] = { 0x10,0x00,0x11 ,0x00 };
unsigned char kcv[4] = { 0x20,0x00,0x21 ,0x00 };
remoteMethods.LoadSymKey(1, 2, 3, 4, 5, keyData, sizeof(keyData), kcv,sizeof(kcv), 10);
std::cout << "OUT pbKCV=" << bytesToHexString(kcv,sizeof(kcv)) << std::endl;
unsigned char info[] = { 0x20,0x00,0x21 ,0x00 };
DWORD infoLen = sizeof(info);
remoteMethods.GetDeviceInfo(info, &infoLen, 5);
std::cout << "OUT info=" << bytesToHexString(info, sizeof(info)) << std::endl;
std::cin.get();
return 0;
}
测试非常好用,30多个接口一天就改好了。