我有以下 .proto
Protobuf
(2.6.1 更详细):
service InstallService {
rpc getWifiNetworks (WifiRequest) returns (WifiResponse);
}
我已经生成了java文件并且我有BlockingStub:
TestInstallService.BlockingInterface service = TestInstallService.newBlockingStub(channel);
我可以使用 if 以阻塞方式(效果很好):
Wifi.WifiResponse response = service.getWifiNetworks(controller, request);
现在我正在创建 C++ 客户端,它也应该以阻塞方式工作,但我在原型和生成的 C++ 代码中都看不到任何
Blocking
接口。如何在Protobuf中用C++生成BlockingStub?如果以异步方式工作,我如何通过关闭?
生成的C++服务文件(.cpp):
class InstallService_Stub;
class InstallService : public ::google::protobuf::Service {
protected:
// This class should be treated as an abstract interface.
inline InstallService() {};
public:
virtual ~InstallService();
typedef InstallService_Stub Stub;
static const ::google::protobuf::ServiceDescriptor* descriptor();
virtual void getWifiNetworks(::google::protobuf::RpcController* controller,
const ::WifiRequest* request,
::WifiResponse* response,
::google::protobuf::Closure* done);
// implements Service ----------------------------------------------
const ::google::protobuf::ServiceDescriptor* GetDescriptor();
void CallMethod(const ::google::protobuf::MethodDescriptor* method,
::google::protobuf::RpcController* controller,
const ::google::protobuf::Message* request,
::google::protobuf::Message* response,
::google::protobuf::Closure* done);
const ::google::protobuf::Message& GetRequestPrototype(
const ::google::protobuf::MethodDescriptor* method) const;
const ::google::protobuf::Message& GetResponsePrototype(
const ::google::protobuf::MethodDescriptor* method) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InstallService);
};
class InstallService_Stub : public InstallService {
public:
InstallService_Stub(::google::protobuf::RpcChannel* channel);
InstallService_Stub(::google::protobuf::RpcChannel* channel,
::google::protobuf::Service::ChannelOwnership ownership);
~InstallService_Stub();
inline ::google::protobuf::RpcChannel* channel() { return channel_; }
// implements InstallService ------------------------------------------
void getWifiNetworks(::google::protobuf::RpcController* controller,
const ::WifiRequest* request,
::WifiResponse* response,
::google::protobuf::Closure* done);
private:
::google::protobuf::RpcChannel* channel_;
bool owns_channel_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InstallService_Stub);
};
看来
protoc
没有生成任何阻塞代码,所以我不得不使用自制的阻塞:
bool callbackFired = false;
void myCallback() {
// ...
callbackFired = true;
}
// run service method
service->myMethod(rpcController, request, response, NewCallback(&myCallback));
// block the thread until callback is invoked
while (!callbackFired);
...
您可以将参数
done
作为 nullptr 发送。
执行此操作的方法是提供您自己的 InstallService 子类,该子类覆盖您要实现的方法:
struct MyInstallService : public InstallService
{
void getWifiNetworks(::google::protobuf::RpcController* controller,
const ::WifiRequest* request,
::WifiResponse* response,
::google::protobuf::Closure* done) override
{
// do your work here
// fill up the response here
done->Run(); // this will trigger the response
}
};
客户端: 像这样的东西
namespace detail {
template<class F>
struct simple_closure : google::protobuf::Closure {
simple_closure(F f)
: _f(std::move(f))
{}
void Run() override {
_f();
}
private:
F _f;
};
}
template<class F>
std::unique_ptr<detail::simple_closure<F>> make_closure(F&& f) {
return std::make_unique<detail::simple_closure<F>>(std::forward<F>(f));
}
std::unique_ptr<WifiResponse> syncGetWifiNetworks(InstallService_Stub & stub, const WifiRequest& req)
{
auto result = std::make_unique<WifiResponse>();
auto promise = std::promise<std::unique_ptr<WifiResponse>>;
auto future = promise.get_future();
auto controller = allocate_controller(); // you need to write this
auto closure = make_closure([&result, &promise]{
promise.set_value(std::move(result));
});
// assumes you already have an async comms queue - otherwise just
// dispatch this lambda to a std::async(std::launch::async, ...)
comms_queue.dispatch([&controller, &req, &stub, &response]{
stub.getWifiNetworks(controller, &req, response.get(), closure);
};
// HERE is where the current thread blocks until the promise is fulfilled
return future.get();
}