一、Cap’n Proto介绍

官网: Cap’n Proto

定义

Cap’n Proto是一款号称具有"infinity faster"的RPC框架。你可以认为它是JSON,只是它直接生成了二进制格式的消息。因为其没有任何encoding/decoding步骤,所以其性能得到大幅提升。由于其异步Promise PipeLine的特点,相比于传统RPC方式实现foo+bar调用,其不需要返回X中间结果,而是一次性将请求发送给Server,Server则只需要返回一个Promise。

四个等级(由低到高)

  1. Object references and promise pipelining
  2. Persistent capabilities
  3. Three-way interactions
  4. Reference equality / joining

五种模式

  1. 1v1单向
    Server提供能力,client使用能力,只能client主动发起对能力的request, server只能被动应答不支持server主动发起request
  2. 1v1双向
    Client和server都提供相同或不同的能力,互相调用对方的能力
  3. Nv1单向
    同一个server可以为多个client提供相同或不同的能力,不支持server主动发起request
  4. Nv1 双向
    同一个server可以为多个client提供相同或不同的能力,Client也可以提供能力给server使用
  5. NvN单双向
    同一个server可以为多个client提供相同或不同的能力,同一个Client也可以提供能力给外部用

    性能提升比较图

通信流程图

时序图

组件图

二、Cap’n Proto编译手顺

1、安装Cap’n Proto所需的GNU自动工具

sudo apt-get install libtool
sudo apt-get install autoconf
sudo apt-get install automake

2、安装Cap'n Proto并安装编译

git clone https://github.com/capnproto/capnproto.git
cd capnproto/c++
autoreconf -i
./configure
make -j6 check
sudo make install

三、Cap’n Proto Sapmle

本例子来源于官方代码,路径如下:
capnproto/c++/samples

1、server和client编写

1.calculator.capnp

@0x85150b117366d14b;interface Calculator {# A "simple" mathematical calculator, callable via RPC.## But, to show off Cap'n Proto, we add some twists:## - You can use the result from one call as the input to the next#   without a network round trip.  To accomplish this, evaluate()#   returns a `Value` object wrapping the actual numeric value.#   This object may be used in a subsequent expression.  With#   promise pipelining, the Value can actually be used before#   the evaluate() call that creates it returns!## - You can define new functions, and then call them.  This again#   shows off pipelining, but it also gives the client the#   opportunity to define a function on the client side and have#   the server call back to it.## - The basic arithmetic operators are exposed as Functions, and#   you have to call getOperator() to obtain them from the server.#   This again demonstrates pipelining -- using getOperator() to#   get each operator and then using them in evaluate() still#   only takes one network round trip.evaluate @0 (expression :Expression) -> (value :Value);# Evaluate the given expression and return the result.  The# result is returned wrapped in a Value interface so that you# may pass it back to the server in a pipelined request.  To# actually get the numeric value, you must call read() on the# Value -- but again, this can be pipelined so that it incurs# no additional latency.struct Expression {# A numeric expression.union {literal @0 :Float64;# A literal numeric value.previousResult @1 :Value;# A value that was (or, will be) returned by a previous# evaluate().parameter @2 :UInt32;# A parameter to the function (only valid in function bodies;# see defFunction).call :group {# Call a function on a list of parameters.function @3 :Function;params @4 :List(Expression);}}}interface Value {# Wraps a numeric value in an RPC object.  This allows the value# to be used in subsequent evaluate() requests without the client# waiting for the evaluate() that returns the Value to finish.read @0 () -> (value :Float64);# Read back the raw numeric value.}defFunction @1 (paramCount :Int32, body :Expression)-> (func :Function);# Define a function that takes `paramCount` parameters and returns the# evaluation of `body` after substituting these parameters.interface Function {# An algebraic function.  Can be called directly, or can be used inside# an Expression.## A client can create a Function that runs on the server side using# `defFunction()` or `getOperator()`.  Alternatively, a client can# implement a Function on the client side and the server will call back# to it.  However, a function defined on the client side will require a# network round trip whenever the server needs to call it, whereas# functions defined on the server and then passed back to it are called# locally.call @0 (params :List(Float64)) -> (value :Float64);# Call the function on the given parameters.}getOperator @2 (op :Operator) -> (func :Function);# Get a Function representing an arithmetic operator, which can then be# used in Expressions.enum Operator {add @0;subtract @1;multiply @2;divide @3;}
}

2.calculator-server.c++

// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.#include "calculator.capnp.h"
#include <kj/debug.h>
#include <capnp/ez-rpc.h>
#include <capnp/message.h>
#include <iostream>typedef unsigned int uint;kj::Promise<double> readValue(Calculator::Value::Client value) {// Helper function to asynchronously call read() on a Calculator::Value and// return a promise for the result.  (In the future, the generated code might// include something like this automatically.)return value.readRequest().send().then([](capnp::Response<Calculator::Value::ReadResults> result) {return result.getValue();});
}kj::Promise<double> evaluateImpl(Calculator::Expression::Reader expression,capnp::List<double>::Reader params = capnp::List<double>::Reader()) {// Implementation of CalculatorImpl::evaluate(), also shared by// FunctionImpl::call().  In the latter case, `params` are the parameter// values passed to the function; in the former case, `params` is just an// empty list.switch (expression.which()) {case Calculator::Expression::LITERAL:return expression.getLiteral();case Calculator::Expression::PREVIOUS_RESULT:return readValue(expression.getPreviousResult());case Calculator::Expression::PARAMETER: {KJ_REQUIRE(expression.getParameter() < params.size(),"Parameter index out-of-range.");return params[expression.getParameter()];}case Calculator::Expression::CALL: {auto call = expression.getCall();auto func = call.getFunction();// Evaluate each parameter.kj::Array<kj::Promise<double>> paramPromises =KJ_MAP(param, call.getParams()) {return evaluateImpl(param, params);};// Join the array of promises into a promise for an array.kj::Promise<kj::Array<double>> joinedParams =kj::joinPromises(kj::mv(paramPromises));// When the parameters are complete, call the function.return joinedParams.then([KJ_CPCAP(func)](kj::Array<double>&& paramValues) mutable {auto request = func.callRequest();request.setParams(paramValues);return request.send().then([](capnp::Response<Calculator::Function::CallResults>&& result) {return result.getValue();});});}default:// Throw an exception.KJ_FAIL_REQUIRE("Unknown expression type.");}
}class ValueImpl final: public Calculator::Value::Server {// Simple implementation of the Calculator.Value Cap'n Proto interface.public:ValueImpl(double value): value(value) {}kj::Promise<void> read(ReadContext context) {context.getResults().setValue(value);return kj::READY_NOW;}private:double value;
};class FunctionImpl final: public Calculator::Function::Server {// Implementation of the Calculator.Function Cap'n Proto interface, where the// function is defined by a Calculator.Expression.public:FunctionImpl(uint paramCount, Calculator::Expression::Reader body): paramCount(paramCount) {this->body.setRoot(body);}kj::Promise<void> call(CallContext context) {auto params = context.getParams().getParams();KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");return evaluateImpl(body.getRoot<Calculator::Expression>(), params).then([KJ_CPCAP(context)](double value) mutable {context.getResults().setValue(value);});}private:uint paramCount;// The function's arity.capnp::MallocMessageBuilder body;// Stores a permanent copy of the function body.
};class OperatorImpl final: public Calculator::Function::Server {// Implementation of the Calculator.Function Cap'n Proto interface, wrapping// basic binary arithmetic operators.public:OperatorImpl(Calculator::Operator op): op(op) {}kj::Promise<void> call(CallContext context) {auto params = context.getParams().getParams();KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");double result;switch (op) {case Calculator::Operator::ADD:     result = params[0] + params[1]; break;case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break;case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break;case Calculator::Operator::DIVIDE:  result = params[0] / params[1]; break;default:KJ_FAIL_REQUIRE("Unknown operator.");}context.getResults().setValue(result);return kj::READY_NOW;}private:Calculator::Operator op;
};class CalculatorImpl final: public Calculator::Server {// Implementation of the Calculator Cap'n Proto interface.public:kj::Promise<void> evaluate(EvaluateContext context) override {return evaluateImpl(context.getParams().getExpression()).then([KJ_CPCAP(context)](double value) mutable {context.getResults().setValue(kj::heap<ValueImpl>(value));});}kj::Promise<void> defFunction(DefFunctionContext context) override {auto params = context.getParams();context.getResults().setFunc(kj::heap<FunctionImpl>(params.getParamCount(), params.getBody()));return kj::READY_NOW;}kj::Promise<void> getOperator(GetOperatorContext context) override {context.getResults().setFunc(kj::heap<OperatorImpl>(context.getParams().getOp()));return kj::READY_NOW;}
};int main(int argc, const char* argv[]) {if (argc != 2) {std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n""Runs the server bound to the given address/port.\n""ADDRESS may be '*' to bind to all local addresses.\n"":PORT may be omitted to choose a port automatically." << std::endl;return 1;}// Set up a server.capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]);// Write the port number to stdout, in case it was chosen automatically.auto& waitScope = server.getWaitScope();uint port = server.getPort().wait(waitScope);if (port == 0) {// The address format "unix:/path/to/socket" opens a unix domain socket,// in which case the port will be zero.std::cout << "Listening on Unix socket..." << std::endl;} else {std::cout << "Listening on port " << port << "..." << std::endl;}// Run forever, accepting connections and handling requests.kj::NEVER_DONE.wait(waitScope);
}

3.calculator-client.c++

#include "calculator.capnp.h"
#include <capnp/ez-rpc.h>
#include <kj/debug.h>
#include <math.h>
#include <iostream>class PowerFunction final: public Calculator::Function::Server {public:kj::Promise<void> call(CallContext context) {auto params = context.getParams().getParams();KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");context.getResults().setValue(pow(params[0], params[1]));return kj::READY_NOW;}
};int main(int argc, const char* argv[]) {if (argc != 2) {std::cerr << "usage: " << argv[0] << " HOST:PORT\n""Connects to the Calculator server at the given address and ""does some RPCs." << std::endl;return 1;}capnp::EzRpcClient client(argv[1]);Calculator::Client calculator = client.getMain<Calculator>();// Keep an eye on `waitScope`.  Whenever you see it used is a place where we// stop and wait for the server to respond.  If a line of code does not use// `waitScope`, then it does not block!auto& waitScope = client.getWaitScope();{// Make a request that just evaluates the literal value 123.//// What's interesting here is that evaluate() returns a "Value", which is// another interface and therefore points back to an object living on the// server.  We then have to call read() on that object to read it.// However, even though we are making two RPC's, this block executes in// *one* network round trip because of promise pipelining:  we do not wait// for the first call to complete before we send the second call to the// server.std::cout << "Evaluating a literal... ";std::cout.flush();// Set up the request.auto request = calculator.evaluateRequest();request.getExpression().setLiteral(123);// Send it, which returns a promise for the result (without blocking).auto evalPromise = request.send();// Using the promise, create a pipelined request to call read() on the// returned object, and then send that.auto readPromise = evalPromise.getValue().readRequest().send();// Now that we've sent all the requests, wait for the response.  Until this// point, we haven't waited at all!auto response = readPromise.wait(waitScope);KJ_ASSERT(response.getValue() == 123);std::cout << "PASS" << std::endl;}{// Make a request to evaluate 123 + 45 - 67.//// The Calculator interface requires that we first call getOperator() to// get the addition and subtraction functions, then call evaluate() to use// them.  But, once again, we can get both functions, call evaluate(), and// then read() the result -- four RPCs -- in the time of *one* network// round trip, because of promise pipelining.std::cout << "Using add and subtract... ";std::cout.flush();Calculator::Function::Client add = nullptr;Calculator::Function::Client subtract = nullptr;{// Get the "add" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::ADD);add = request.send().getFunc();}{// Get the "subtract" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::SUBTRACT);subtract = request.send().getFunc();}// Build the request to evaluate 123 + 45 - 67.auto request = calculator.evaluateRequest();auto subtractCall = request.getExpression().initCall();subtractCall.setFunction(subtract);auto subtractParams = subtractCall.initParams(2);subtractParams[1].setLiteral(67);auto addCall = subtractParams[0].initCall();addCall.setFunction(add);auto addParams = addCall.initParams(2);addParams[0].setLiteral(123);addParams[1].setLiteral(45);// Send the evaluate() request, read() the result, and wait for read() to// finish.auto evalPromise = request.send();auto readPromise = evalPromise.getValue().readRequest().send();auto response = readPromise.wait(waitScope);KJ_ASSERT(response.getValue() == 101);std::cout << "PASS" << std::endl;}{// Make a request to evaluate 4 * 6, then use the result in two more// requests that add 3 and 5.//// Since evaluate() returns its result wrapped in a `Value`, we can pass// that `Value` back to the server in subsequent requests before the first// `evaluate()` has actually returned.  Thus, this example again does only// one network round trip.std::cout << "Pipelining eval() calls... ";std::cout.flush();Calculator::Function::Client add = nullptr;Calculator::Function::Client multiply = nullptr;{// Get the "add" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::ADD);add = request.send().getFunc();}{// Get the "multiply" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::MULTIPLY);multiply = request.send().getFunc();}// Build the request to evaluate 4 * 6auto request = calculator.evaluateRequest();auto multiplyCall = request.getExpression().initCall();multiplyCall.setFunction(multiply);auto multiplyParams = multiplyCall.initParams(2);multiplyParams[0].setLiteral(4);multiplyParams[1].setLiteral(6);auto multiplyResult = request.send().getValue();// Use the result in two calls that add 3 and add 5.auto add3Request = calculator.evaluateRequest();auto add3Call = add3Request.getExpression().initCall();add3Call.setFunction(add);auto add3Params = add3Call.initParams(2);add3Params[0].setPreviousResult(multiplyResult);add3Params[1].setLiteral(3);auto add3Promise = add3Request.send().getValue().readRequest().send();auto add5Request = calculator.evaluateRequest();auto add5Call = add5Request.getExpression().initCall();add5Call.setFunction(add);auto add5Params = add5Call.initParams(2);add5Params[0].setPreviousResult(multiplyResult);add5Params[1].setLiteral(5);auto add5Promise = add5Request.send().getValue().readRequest().send();// Now wait for the results.KJ_ASSERT(add3Promise.wait(waitScope).getValue() == 27);KJ_ASSERT(add5Promise.wait(waitScope).getValue() == 29);std::cout << "PASS" << std::endl;}{// Our calculator interface supports defining functions.  Here we use it// to define two functions and then make calls to them as follows:////   f(x, y) = x * 100 + y//   g(x) = f(x, x + 1) * 2;//   f(12, 34)//   g(21)//// Once again, the whole thing takes only one network round trip.std::cout << "Defining functions... ";std::cout.flush();Calculator::Function::Client add = nullptr;Calculator::Function::Client multiply = nullptr;Calculator::Function::Client f = nullptr;Calculator::Function::Client g = nullptr;{// Get the "add" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::ADD);add = request.send().getFunc();}{// Get the "multiply" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::MULTIPLY);multiply = request.send().getFunc();}{// Define f.auto request = calculator.defFunctionRequest();request.setParamCount(2);{// Build the function body.auto addCall = request.getBody().initCall();addCall.setFunction(add);auto addParams = addCall.initParams(2);addParams[1].setParameter(1);  // yauto multiplyCall = addParams[0].initCall();multiplyCall.setFunction(multiply);auto multiplyParams = multiplyCall.initParams(2);multiplyParams[0].setParameter(0);  // xmultiplyParams[1].setLiteral(100);}f = request.send().getFunc();}{// Define g.auto request = calculator.defFunctionRequest();request.setParamCount(1);{// Build the function body.auto multiplyCall = request.getBody().initCall();multiplyCall.setFunction(multiply);auto multiplyParams = multiplyCall.initParams(2);multiplyParams[1].setLiteral(2);auto fCall = multiplyParams[0].initCall();fCall.setFunction(f);auto fParams = fCall.initParams(2);fParams[0].setParameter(0);auto addCall = fParams[1].initCall();addCall.setFunction(add);auto addParams = addCall.initParams(2);addParams[0].setParameter(0);addParams[1].setLiteral(1);}g = request.send().getFunc();}// OK, we've defined all our functions.  Now create our eval requests.// f(12, 34)auto fEvalRequest = calculator.evaluateRequest();auto fCall = fEvalRequest.initExpression().initCall();fCall.setFunction(f);auto fParams = fCall.initParams(2);fParams[0].setLiteral(12);fParams[1].setLiteral(34);auto fEvalPromise = fEvalRequest.send().getValue().readRequest().send();// g(21)auto gEvalRequest = calculator.evaluateRequest();auto gCall = gEvalRequest.initExpression().initCall();gCall.setFunction(g);gCall.initParams(1)[0].setLiteral(21);auto gEvalPromise = gEvalRequest.send().getValue().readRequest().send();// Wait for the results.KJ_ASSERT(fEvalPromise.wait(waitScope).getValue() == 1234);KJ_ASSERT(gEvalPromise.wait(waitScope).getValue() == 4244);std::cout << "PASS" << std::endl;}{// Make a request that will call back to a function defined locally.//// Specifically, we will compute 2^(4 + 5).  However, exponent is not// defined by the Calculator server.  So, we'll implement the Function// interface locally and pass it to the server for it to use when// evaluating the expression.//// This example requires two network round trips to complete, because the// server calls back to the client once before finishing.  In this// particular case, this could potentially be optimized by using a tail// call on the server side -- see CallContext::tailCall().  However, to// keep the example simpler, we haven't implemented this optimization in// the sample server.std::cout << "Using a callback... ";std::cout.flush();Calculator::Function::Client add = nullptr;{// Get the "add" function from the server.auto request = calculator.getOperatorRequest();request.setOp(Calculator::Operator::ADD);add = request.send().getFunc();}// Build the eval request for 2^(4+5).auto request = calculator.evaluateRequest();auto powCall = request.getExpression().initCall();powCall.setFunction(kj::heap<PowerFunction>());auto powParams = powCall.initParams(2);powParams[0].setLiteral(2);auto addCall = powParams[1].initCall();addCall.setFunction(add);auto addParams = addCall.initParams(2);addParams[0].setLiteral(4);addParams[1].setLiteral(5);// Send the request and wait.auto response = request.send().getValue().readRequest().send().wait(waitScope);KJ_ASSERT(response.getValue() == 512);std::cout << "PASS" << std::endl;}return 0;
}

2、server和client编译运行(CMake)

project("Cap'n Proto Samples" CXX)
cmake_minimum_required(VERSION 3.1)find_package(CapnProto CONFIG REQUIRED)if(TARGET CapnProto::capnp-rpc)capnp_generate_cpp(calculatorSources calculatorHeaders calculator.capnp)add_executable(calculator-client calculator-client.cpp ${calculatorSources})add_executable(calculator-server calculator-server.cpp ${calculatorSources})target_link_libraries(calculator-client PRIVATE CapnProto::capnp-rpc)target_link_libraries(calculator-server PRIVATE CapnProto::capnp-rpc)target_include_directories(calculator-client PRIVATE ${CMAKE_CURRENT_BINARY_DIR})target_include_directories(calculator-server PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()

3、测试运行

test.sh

capnpc -oc++ calculator.capnp
c++ -std=c++14 -Wall calculator-client.c++ calculator.capnp.c++ \$(pkg-config --cflags --libs capnp-rpc) -o calculator-client
c++ -std=c++14 -Wall calculator-server.c++ calculator.capnp.c++ \$(pkg-config --cflags --libs capnp-rpc) -o calculator-server
rm -f /tmp/capnp-calculator-example-$$
./calculator-server unix:/tmp/capnp-calculator-example-$$ &
sleep 0.1
./calculator-client unix:/tmp/capnp-calculator-example-$$
kill %+
wait %+ || true
rm calculator-client calculator-server calculator.capnp.c++ calculator.capnp.h /tmp/capnp-calculator-example-$$

四、Cap’n Proto Multi-stream Flow Control

官网: Cap’n Proto 0.8 Multi-stream Flow Control

1、server和client编写

1.MyInterface.capnp

@0x9b7efa693ff3d429;interface MyInterface {streamingCall @0 (callback:Callback) -> (chunk:Data);struct Callback {literal @0 :Float64;}interface Data {read @0 () -> (data:Float64);}
}

2.server.c++

#include "MyInterface.capnp.h"
#include <kj/debug.h>
#include <capnp/ez-rpc.h>
#include <capnp/message.h>
#include <iostream>typedef unsigned int uint;kj::Promise<double> readData(MyInterface::Data::Client data)
{// Helper function to asynchronously call read() on a Calculator::Data and// return a promise for the result.  (In the future, the generated code might// include something like this automatically.)return data.readRequest().send().then([](capnp::Response<MyInterface::Data::ReadResults> result){ return result.getData(); });
}class DataImpl final : public MyInterface::Data::Server
{// Simple implementation of the MyInterface.Data Cap'n Proto interface.
public:DataImpl(double data) : data(data) {}kj::Promise<void> read(ReadContext context){context.getResults().setData(data);return kj::READY_NOW;}private:double data;
};kj::Promise<double> streamingCallImpl(MyInterface::Callback::Reader data,capnp::List<double>::Reader params = capnp::List<double>::Reader())
{// Implementation of MyInterfaceImpl::streamingCall(), also shared by// FunctionImpl::call().return data.getLiteral();
}class MyInterfaceImpl final : public MyInterface::Server
{// Implementation of the MyInterface Cap'n Proto interface.
public:kj::Promise<void> streamingCall(StreamingCallContext context) override{return streamingCallImpl(context.getParams().getCallback()).then([KJ_CPCAP(context)](double value) mutable{ context.getResults().setChunk(kj::heap<DataImpl>(value)); });}
};int main(int argc, const char *argv[])
{// Set up a server.capnp::EzRpcServer server(kj::heap<MyInterfaceImpl>(), argv[1]);// Write the port number to stdout, in case it was chosen automatically.auto &waitScope = server.getWaitScope();uint port = server.getPort().wait(waitScope);if (port == 0){ // The address format "unix:/path/to/socket" opens a unix domain socket,// in which case the port will be zero.std::cout << "Listening on Unix socket..." << std::endl;}else{std::cout << "Listening on port " << port << "..." << std::endl;}// Run forever, accepting connections and handling requests.kj::NEVER_DONE.wait(waitScope);
}

3.client.c++

#include "MyInterface.capnp.h"
#include <capnp/ez-rpc.h>
#include <kj/debug.h>
#include <iostream>
#include <unistd.h>class MyInterfaceImpl final : public MyInterface::Server
{// An implementation of the Function interface wrapping pow().  Note that// we're implementing this on the client side and will pass a reference to// the server.  The server will then be able to make calls back to the client.public:kj::Promise<void> streamingCall(StreamingCallContext context){std::cerr << "client sendChunk" << std::endl;return kj::READY_NOW;}
};int main(int argc, const char *argv[])
{if (argc != 2){std::cerr << "usage: " << argv[0] << " HOST:PORT\n""Connects to the MyInterface server at the given address and ""does some RPCs."<< std::endl;return 1;}capnp::EzRpcClient client(argv[1]);MyInterface::Client myinterface = client.getMain<MyInterface>();// Keep an eye on `waitScope`.  Whenever you see it used is a place where we// stop and wait for the server to respond.  If a line of code does not use// `waitScope`, then it does not block!auto &waitScope = client.getWaitScope();{// Make a request that just evaluates the literal value 123.std::cout << "Evaluating a literal... ";std::cout.flush();// //Set up requestauto request = myinterface.streamingCallRequest();request.getCallback().setLiteral(123);// Send it, which returns a promise for the result (without blockingauto evalPromsie = request.send();// Using the promise, create a pipelined request to call read() on the// returned object, and then send that.auto readPromise = evalPromsie.getChunk().readRequest().send();// Now that we've sent all the requests, wait for the response.  Until this// point, we haven't waited at all!auto response = readPromise.wait(waitScope);KJ_ASSERT(response.getData() == 123);std::cout << "PASS" << std::endl;}return 0;
}

2、server和client编译运行(CMake)

4.CMakeLists.txt

project("Multi-stream Flow Control Samples" CXX)
cmake_minimum_required(VERSION 3.1)find_package(CapnProto CONFIG REQUIRED)if(TARGET CapnProto::capnp-rpc)capnp_generate_cpp(MyInterfaceSources MyInterfaceHeaders MyInterface.capnp)add_executable(client client.c++ ${MyInterfaceSources})add_executable(server server.c++ ${MyInterfaceSources})target_link_libraries(client PRIVATE CapnProto::capnp-rpc)target_link_libraries(server PRIVATE CapnProto::capnp-rpc)target_include_directories(client PRIVATE ${CMAKE_CURRENT_BINARY_DIR})target_include_directories(server PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()

3、测试运行

5.test.sh

capnpc -oc++ MyInterface.capnp
c++ -std=c++14 -Wall client.c++ MyInterface.capnp.c++ \$(pkg-config --cflags --libs capnp-rpc) -o client
c++ -std=c++14 -Wall server.c++ MyInterface.capnp.c++ \$(pkg-config --cflags --libs capnp-rpc) -o server
rm -f /tmp/capnp-MyInterface-example-$$
./server unix:/tmp/capnp-MyInterface-example-$$ &
sleep 0.1
./client unix:/tmp/capnp-MyInterface-example-$$
kill %+
wait %+ || true
rm client server MyInterface.capnp.c++ MyInterface.capnp.h /tmp/capnp-MyInterface-example-$$

Cap‘n Proto 介绍(有例子)相关推荐

  1. C++:线程操作之CRITICAL_SECTION用法的介绍和例子理解

    CRITICAL_SECTION 介绍 实例编辑1 示例 实例编辑2 介绍 CRITICAL_SECTION是每个线程中访问临界资源的那段代码,不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地 ...

  2. 最好的EM算法介绍-由例子介绍原理

    版权声明: 本文系转载,由zouxy09所有,发布于http://blog.csdn.net/zouxy09.如果转载,请尊重作者的版权所有,注明出处.如果有问题,请联系作者 zouxy09@qq.c ...

  3. SQL 数据库 学习 017 查询-00 介绍 scott 例子库

    我的电脑系统:Windows 10 64位 SQL Server 软件版本: SQL Server 2014 Express 我们要查数据,总先得有数据要查吧,所以我们需要先找一个例子库.Orale软 ...

  4. 【python】zip 介绍和例子

    介绍 在 Python 教程的这一章中,我们将讨论美妙且极其有用的功能"zip".不幸的是,Python 的初学者经常被 zip 不必要地混淆和害怕.首先这个名字很混乱.许多人将 ...

  5. (转)安富莱stm32 pid介绍以例子

    第15章 ControllerFunctions的使用(PID控制) 本期教程主要讲解控制函数中PID部分,PID的应用十分广泛,希望初学的同学认真学习这部分知识. 15.1 PID介绍 15.2 P ...

  6. 推荐算法的介绍+简单例子

    推荐算法介绍 推荐算法: ​ 推荐算法是计算机专业中的一种算法,通过一些数学算法,推测出用户可能喜欢的东西,目前应用推荐算法比较好的地方主要是网络,其中淘宝做的比较好. 发展背景: ​ 推荐算法的研究 ...

  7. java layoutinflater_LayoutInflater介绍及例子

    复制到剪贴板Java代码 Java代码 //基本用法 publicvoidshowCustomDialog(){ AlertDialog.Builder builder; AlertDialog al ...

  8. UML类图最生动的介绍和例子

    目的 这篇文章主要是介绍如何使用UML类图,目的是让我们能读懂类图,以及上手使用类图. 为什么要使用UML类图 UML类图是一种统一建模语言,可以精准的描述类中拥有的属性以及方法,以及类与类之间的关系 ...

  9. Axis2介绍和例子

    前言 Axis2是目前比较流行的WebService引擎.WebService被应用在很多不同的场景.例如,可以使用WebService来发布服务端 Java类的方法,以便使用不同的客户端进行调用.这 ...

最新文章

  1. tftp:timeout问题解决 - 从Windows传输文件到开发板
  2. 1045 Favorite Color Stripe(LIS解法)
  3. JavaScript中实现函数重载和参数默认值
  4. 一文告诉你,NIPS 2017有多火爆 | 附PPT、视频、代码大总结
  5. pycharm配置git版本管理
  6. ObservableCollection 类 详解
  7. vue-cli中的webpack的config配置详细说明
  8. list和tuple
  9. linux分区表导出与恢复,Linux下硬盘数据恢复与分区表恢复
  10. 管理者和领导者的区别_见到一个领导者时如何识别
  11. java中this_JAVA中this用法小结
  12. sheet中没有getcolumns()方法吗_家庭亲子教育中的八种方法,你做到了吗?
  13. 【GitHub】提交新项目、更新已有的项目
  14. 个性化新闻文章推荐的上下文Bandit方法
  15. django3与vue3本地搭建
  16. 业务逻辑实现方式的讨论:存储过程 good or bad?
  17. 存储过程创建临时表_【松勤教育】MySQL如何创建存储过程
  18. Ascii完整码表(256个)
  19. 集成海康威视Sadp SDK实现修改设备网络参数
  20. 在 vmware ESXi上安装mac系统虚拟机

热门文章

  1. 文章阅读Non-Salient Region Object Mining for Weakly Supervised Semantic Segmentation
  2. 吸管工具,canvas模拟吸管工具,吸取图片中的颜色值 vue.js 颜色值 转换
  3. Vite+Vue3+TypeScript 搭建开发脚手架
  4. img标签以及超链接
  5. 关于win10 遇到无限重启问题-你的电脑将在一分钟后自动重启
  6. 面试!什么是数据仓库?
  7. 2021年中国化妆刷市场趋势报告、技术动态创新及2027年市场预测
  8. USACO The castle 小白代码-供参考(会不断更改代码)
  9. 生产者消费者问题-代码详解(Java多线程)
  10. 23种设计模式-抽象工厂模式介绍加实战代码