cannam@133
|
1 ---
|
cannam@133
|
2 layout: page
|
cannam@133
|
3 title: C++ RPC
|
cannam@133
|
4 ---
|
cannam@133
|
5
|
cannam@133
|
6 # C++ RPC
|
cannam@133
|
7
|
cannam@133
|
8 The Cap'n Proto C++ RPC layer sits on top of the [serialization layer](cxx.html) and implements
|
cannam@133
|
9 the [RPC protocol](rpc.html).
|
cannam@133
|
10
|
cannam@133
|
11 ## Current Status
|
cannam@133
|
12
|
cannam@133
|
13 As of version 0.4, Cap'n Proto's C++ RPC implementation is a [Level 1](rpc.html#protocol-features)
|
cannam@133
|
14 implementation. Persistent capabilities, three-way introductions, and distributed equality are
|
cannam@133
|
15 not yet implemented.
|
cannam@133
|
16
|
cannam@133
|
17 ## Sample Code
|
cannam@133
|
18
|
cannam@133
|
19 The [Calculator example](https://github.com/sandstorm-io/capnproto/tree/master/c++/samples) implements
|
cannam@133
|
20 a fully-functional Cap'n Proto client and server.
|
cannam@133
|
21
|
cannam@133
|
22 ## KJ Concurrency Framework
|
cannam@133
|
23
|
cannam@133
|
24 RPC naturally requires a notion of concurrency. Unfortunately,
|
cannam@133
|
25 [all concurrency models suck](https://plus.google.com/u/0/+KentonVarda/posts/D95XKtB5DhK).
|
cannam@133
|
26
|
cannam@133
|
27 Cap'n Proto's RPC is based on the [KJ library](cxx.html#kj-library)'s event-driven concurrency
|
cannam@133
|
28 framework. The core of the KJ asynchronous framework (events, promises, callbacks) is defined in
|
cannam@133
|
29 `kj/async.h`, with I/O interfaces (streams, sockets, networks) defined in `kj/async-io.h`.
|
cannam@133
|
30
|
cannam@133
|
31 ### Event Loop Concurrency
|
cannam@133
|
32
|
cannam@133
|
33 KJ's concurrency model is based on event loops. While multiple threads are allowed, each thread
|
cannam@133
|
34 must have its own event loop. KJ discourages fine-grained interaction between threads as
|
cannam@133
|
35 synchronization is expensive and error-prone. Instead, threads are encouraged to communicate
|
cannam@133
|
36 through Cap'n Proto RPC.
|
cannam@133
|
37
|
cannam@133
|
38 KJ's event loop model bears a lot of similarity to the Javascript concurrency model. Experienced
|
cannam@133
|
39 Javascript hackers -- especially node.js hackers -- will feel right at home.
|
cannam@133
|
40
|
cannam@133
|
41 _As of version 0.4, the only supported way to communicate between threads is over pipes or
|
cannam@133
|
42 socketpairs. This will be improved in future versions. For now, just set up an RPC connection
|
cannam@133
|
43 over that socketpair. :)_
|
cannam@133
|
44
|
cannam@133
|
45 ### Promises
|
cannam@133
|
46
|
cannam@133
|
47 Function calls that do I/O must do so asynchronously, and must return a "promise" for the
|
cannam@133
|
48 result. Promises -- also known as "futures" in some systems -- are placeholders for the results
|
cannam@133
|
49 of operations that have not yet completed. When the operation completes, we say that the promise
|
cannam@133
|
50 "resolves" to a value, or is "fulfilled". A promise can also be "rejected", which means an
|
cannam@133
|
51 exception occurred.
|
cannam@133
|
52
|
cannam@133
|
53 {% highlight c++ %}
|
cannam@133
|
54 // Example promise-based interfaces.
|
cannam@133
|
55
|
cannam@133
|
56 kj::Promise<kj::String> fetchHttp(kj::StringPtr url);
|
cannam@133
|
57 // Asynchronously fetches an HTTP document and returns
|
cannam@133
|
58 // the content as a string.
|
cannam@133
|
59
|
cannam@133
|
60 kj::Promise<void> sendEmail(kj::StringPtr address,
|
cannam@133
|
61 kj::StringPtr title, kj::StringPtr body);
|
cannam@133
|
62 // Sends an e-mail to the given address with the given title
|
cannam@133
|
63 // and body. The returned promise resolves (to nothing) when
|
cannam@133
|
64 // the message has been successfully sent.
|
cannam@133
|
65 {% endhighlight %}
|
cannam@133
|
66
|
cannam@133
|
67 As you will see, KJ promises are very similar to the evolving Javascript promise standard, and
|
cannam@133
|
68 much of the [wisdom around it](https://www.google.com/search?q=javascript+promises) can be directly
|
cannam@133
|
69 applied to KJ promises.
|
cannam@133
|
70
|
cannam@133
|
71 ### Callbacks
|
cannam@133
|
72
|
cannam@133
|
73 If you want to do something with the result of a promise, you must first wait for it to complete.
|
cannam@133
|
74 This is normally done by registering a callback to execute on completion. Luckily, C++11 just
|
cannam@133
|
75 introduced lambdas, which makes this far more pleasant than it would have been a few years ago!
|
cannam@133
|
76
|
cannam@133
|
77 {% highlight c++ %}
|
cannam@133
|
78 kj::Promise<kj::String> contentPromise =
|
cannam@133
|
79 fetchHttp("http://example.com");
|
cannam@133
|
80
|
cannam@133
|
81 kj::Promise<int> lineCountPromise =
|
cannam@133
|
82 contentPromise.then([](kj::String&& content) {
|
cannam@133
|
83 return countChars(content, '\n');
|
cannam@133
|
84 });
|
cannam@133
|
85 {% endhighlight %}
|
cannam@133
|
86
|
cannam@133
|
87 The callback passed to `then()` takes the promised result as its parameter and returns a new value.
|
cannam@133
|
88 `then()` itself returns a new promise for that value which the callback will eventually return.
|
cannam@133
|
89 If the callback itself returns a promise, then `then()` actually returns a promise for the
|
cannam@133
|
90 resolution of the latter promise -- that is, `Promise<Promise<T>>` is automatically reduced to
|
cannam@133
|
91 `Promise<T>`.
|
cannam@133
|
92
|
cannam@133
|
93 Note that `then()` consumes the original promise: you can only call `then()` once. This is true
|
cannam@133
|
94 of all of the methods of `Promise`. The only way to consume a promise in multiple places is to
|
cannam@133
|
95 first "fork" it with the `fork()` method, which we don't get into here. Relatedly, promises
|
cannam@133
|
96 are linear types, which means they have move constructors but not copy constructors.
|
cannam@133
|
97
|
cannam@133
|
98 ### Error Propagation
|
cannam@133
|
99
|
cannam@133
|
100 `then()` takes an optional second parameter for handling errors. Think of this like a `catch`
|
cannam@133
|
101 block.
|
cannam@133
|
102
|
cannam@133
|
103 {% highlight c++ %}
|
cannam@133
|
104 kj::Promise<int> lineCountPromise =
|
cannam@133
|
105 promise.then([](kj::String&& content) {
|
cannam@133
|
106 return countChars(content, '\n');
|
cannam@133
|
107 }, [](kj::Exception&& exception) {
|
cannam@133
|
108 // Error! Pretend the document was empty.
|
cannam@133
|
109 return 0;
|
cannam@133
|
110 });
|
cannam@133
|
111 {% endhighlight %}
|
cannam@133
|
112
|
cannam@133
|
113 Note that the KJ framework coerces all exceptions to `kj::Exception` -- the exception's description
|
cannam@133
|
114 (as returned by `what()`) will be retained, but any type-specific information is lost. Under KJ
|
cannam@133
|
115 exception philosophy, exceptions always represent an error that should not occur under normal
|
cannam@133
|
116 operation, and the only purpose of exceptions is to make software fault-tolerant. In particular,
|
cannam@133
|
117 the only reasonable ways to handle an exception are to try again, tell a human, and/or propagate
|
cannam@133
|
118 to the caller. To that end, `kj::Exception` contains information useful for reporting purposes
|
cannam@133
|
119 and to help decide if trying again is reasonable, but typed exception hierarchies are not useful
|
cannam@133
|
120 and not supported.
|
cannam@133
|
121
|
cannam@133
|
122 It is recommended that Cap'n Proto code use the assertion macros in `kj/debug.h` to throw
|
cannam@133
|
123 exceptions rather than use the C++ `throw` keyword. These macros make it easy to add useful
|
cannam@133
|
124 debug information to an exception and generally play nicely with the KJ framework. In fact, you
|
cannam@133
|
125 can even use these macros -- and propagate exceptions through promises -- if you compile your code
|
cannam@133
|
126 with exceptions disabled. See the headers for more information.
|
cannam@133
|
127
|
cannam@133
|
128 ### Waiting
|
cannam@133
|
129
|
cannam@133
|
130 It is illegal for code running in an event callback to wait, since this would stall the event loop.
|
cannam@133
|
131 However, if you are the one responsible for starting the event loop in the first place, then KJ
|
cannam@133
|
132 makes it easy to say "run the event loop until this promise resolves, then return the result".
|
cannam@133
|
133
|
cannam@133
|
134 {% highlight c++ %}
|
cannam@133
|
135 kj::EventLoop loop;
|
cannam@133
|
136 kj::WaitScope waitScope(loop);
|
cannam@133
|
137
|
cannam@133
|
138 kj::Promise<kj::String> contentPromise =
|
cannam@133
|
139 fetchHttp("http://example.com");
|
cannam@133
|
140
|
cannam@133
|
141 kj::String content = contentPromise.wait(waitScope);
|
cannam@133
|
142
|
cannam@133
|
143 int lineCount = countChars(content, '\n');
|
cannam@133
|
144 {% endhighlight %}
|
cannam@133
|
145
|
cannam@133
|
146 Using `wait()` is common in high-level client-side code. On the other hand, it is almost never
|
cannam@133
|
147 used in servers.
|
cannam@133
|
148
|
cannam@133
|
149 ### Cancellation
|
cannam@133
|
150
|
cannam@133
|
151 If you discard a `Promise` without calling any of its methods, the operation it was waiting for
|
cannam@133
|
152 is canceled, because the `Promise` itself owns that operation. This means than any pending
|
cannam@133
|
153 callbacks simply won't be executed. If you need explicit notification when a promise is canceled,
|
cannam@133
|
154 you can use its `attach()` method to attach an object with a destructor -- the destructor will be
|
cannam@133
|
155 called when the promise either completes or is canceled.
|
cannam@133
|
156
|
cannam@133
|
157 ### Other Features
|
cannam@133
|
158
|
cannam@133
|
159 KJ supports a number of primitive operations that can be performed on promises. The complete API
|
cannam@133
|
160 is documented directly in the `kj/async.h` header. Additionally, see the `kj/async-io.h` header
|
cannam@133
|
161 for APIs for performing basic network I/O -- although Cap'n Proto RPC users typically won't need
|
cannam@133
|
162 to use these APIs directly.
|
cannam@133
|
163
|
cannam@133
|
164 ## Generated Code
|
cannam@133
|
165
|
cannam@133
|
166 Imagine the following interface:
|
cannam@133
|
167
|
cannam@133
|
168 {% highlight capnp %}
|
cannam@133
|
169 interface Directory {
|
cannam@133
|
170 create @0 (name :Text) -> (file :File);
|
cannam@133
|
171 open @1 (name :Text) -> (file :File);
|
cannam@133
|
172 remove @2 (name :Text);
|
cannam@133
|
173 }
|
cannam@133
|
174 {% endhighlight %}
|
cannam@133
|
175
|
cannam@133
|
176 `capnp compile` will generate code that looks like this (edited for readability):
|
cannam@133
|
177
|
cannam@133
|
178 {% highlight c++ %}
|
cannam@133
|
179 struct Directory {
|
cannam@133
|
180 Directory() = delete;
|
cannam@133
|
181
|
cannam@133
|
182 class Client;
|
cannam@133
|
183 class Server;
|
cannam@133
|
184
|
cannam@133
|
185 struct CreateParams;
|
cannam@133
|
186 struct CreateResults;
|
cannam@133
|
187 struct OpenParams;
|
cannam@133
|
188 struct OpenResults;
|
cannam@133
|
189 struct RemoveParams;
|
cannam@133
|
190 struct RemoveResults;
|
cannam@133
|
191 // Each of these is equivalent to what would be generated for
|
cannam@133
|
192 // a Cap'n Proto struct with one field for each parameter /
|
cannam@133
|
193 // result.
|
cannam@133
|
194 };
|
cannam@133
|
195
|
cannam@133
|
196 class Directory::Client
|
cannam@133
|
197 : public virtual capnp::Capability::Client {
|
cannam@133
|
198 public:
|
cannam@133
|
199 Client(std::nullptr_t);
|
cannam@133
|
200 Client(kj::Own<Directory::Server> server);
|
cannam@133
|
201 Client(kj::Promise<Client> promise);
|
cannam@133
|
202 Client(kj::Exception exception);
|
cannam@133
|
203
|
cannam@133
|
204 capnp::Request<CreateParams, CreateResults> createRequest();
|
cannam@133
|
205 capnp::Request<OpenParams, OpenResults> openRequest();
|
cannam@133
|
206 capnp::Request<RemoveParams, RemoveResults> removeRequest();
|
cannam@133
|
207 };
|
cannam@133
|
208
|
cannam@133
|
209 class Directory::Server
|
cannam@133
|
210 : public virtual capnp::Capability::Server {
|
cannam@133
|
211 protected:
|
cannam@133
|
212 typedef capnp::CallContext<CreateParams, CreateResults> CreateContext;
|
cannam@133
|
213 typedef capnp::CallContext<OpenParams, OpenResults> OpenContext;
|
cannam@133
|
214 typedef capnp::CallContext<RemoveParams, RemoveResults> RemoveContext;
|
cannam@133
|
215 // Convenience typedefs.
|
cannam@133
|
216
|
cannam@133
|
217 virtual kj::Promise<void> create(CreateContext context);
|
cannam@133
|
218 virtual kj::Promise<void> open(OpenContext context);
|
cannam@133
|
219 virtual kj::Promise<void> remove(RemoveContext context);
|
cannam@133
|
220 // Methods for you to implement.
|
cannam@133
|
221 };
|
cannam@133
|
222 {% endhighlight %}
|
cannam@133
|
223
|
cannam@133
|
224 ### Clients
|
cannam@133
|
225
|
cannam@133
|
226 The generated `Client` type represents a reference to a remote `Server`. `Client`s are
|
cannam@133
|
227 pass-by-value types that use reference counting under the hood. (Warning: For performance
|
cannam@133
|
228 reasons, the reference counting used by `Client`s is not thread-safe, so you must not copy a
|
cannam@133
|
229 `Client` to another thread, unless you do it by means of an inter-thread RPC.)
|
cannam@133
|
230
|
cannam@133
|
231 A `Client` can be implicitly constructed from any of:
|
cannam@133
|
232
|
cannam@133
|
233 * A `kj::Own<Server>`, which takes ownership of the server object and creates a client that
|
cannam@133
|
234 calls it. (You can get a `kj::Own<T>` to a newly-allocated heap object using
|
cannam@133
|
235 `kj::heap<T>(constructorParams)`; see `kj/memory.h`.)
|
cannam@133
|
236 * A `kj::Promise<Client>`, which creates a client whose methods first wait for the promise to
|
cannam@133
|
237 resolve, then forward the call to the resulting client.
|
cannam@133
|
238 * A `kj::Exception`, which creates a client whose methods always throw that exception.
|
cannam@133
|
239 * `nullptr`, which creates a client whose methods always throw. This is meant to be used to
|
cannam@133
|
240 initialize variables that will be initialized to a real value later on.
|
cannam@133
|
241
|
cannam@133
|
242 For each interface method `foo()`, the `Client` has a method `fooRequest()` which creates a new
|
cannam@133
|
243 request to call `foo()`. The returned `capnp::Request` object has methods equivalent to a
|
cannam@133
|
244 `Builder` for the parameter struct (`FooParams`), with the addition of a method `send()`.
|
cannam@133
|
245 `send()` sends the RPC and returns a `capnp::RemotePromise<FooResults>`.
|
cannam@133
|
246
|
cannam@133
|
247 This `RemotePromise` is equivalent to `kj::Promise<capnp::Response<FooResults>>`, but also has
|
cannam@133
|
248 methods that allow pipelining. Namely:
|
cannam@133
|
249
|
cannam@133
|
250 * For each interface-typed result, it has a getter method which returns a `Client` of that type.
|
cannam@133
|
251 Calling this client will send a pipelined call to the server.
|
cannam@133
|
252 * For each struct-typed result, it has a getter method which returns an object containing pipeline
|
cannam@133
|
253 getters for that struct's fields.
|
cannam@133
|
254
|
cannam@133
|
255 In other words, the `RemotePromise` effectively implements a subset of the eventual results'
|
cannam@133
|
256 `Reader` interface -- one that only allows access to interfaces and sub-structs.
|
cannam@133
|
257
|
cannam@133
|
258 The `RemotePromise` eventually resolves to `capnp::Response<FooResults>`, which behaves like a
|
cannam@133
|
259 `Reader` for the result struct except that it also owns the result message.
|
cannam@133
|
260
|
cannam@133
|
261 {% highlight c++ %}
|
cannam@133
|
262 Directory::Client dir = ...;
|
cannam@133
|
263
|
cannam@133
|
264 // Create a new request for the `open()` method.
|
cannam@133
|
265 auto request = dir.openRequest();
|
cannam@133
|
266 request.setName("foo");
|
cannam@133
|
267
|
cannam@133
|
268 // Send the request.
|
cannam@133
|
269 auto promise = request.send();
|
cannam@133
|
270
|
cannam@133
|
271 // Make a pipelined request.
|
cannam@133
|
272 auto promise2 = promise.getFile().getSizeRequest().send();
|
cannam@133
|
273
|
cannam@133
|
274 // Wait for the full results.
|
cannam@133
|
275 auto promise3 = promise2.then(
|
cannam@133
|
276 [](capnp::Response<File::GetSizeResults>&& response) {
|
cannam@133
|
277 cout << "File size is: " << response.getSize() << endl;
|
cannam@133
|
278 });
|
cannam@133
|
279 {% endhighlight %}
|
cannam@133
|
280
|
cannam@133
|
281 For [generic methods](language.html#generic-methods), the `fooRequest()` method will be a template;
|
cannam@133
|
282 you must explicitly specify type parameters.
|
cannam@133
|
283
|
cannam@133
|
284 ### Servers
|
cannam@133
|
285
|
cannam@133
|
286 The generated `Server` type is an abstract interface which may be subclassed to implement a
|
cannam@133
|
287 capability. Each method takes a `context` argument and returns a `kj::Promise<void>` which
|
cannam@133
|
288 resolves when the call is finished. The parameter and result structures are accessed through the
|
cannam@133
|
289 context -- `context.getParams()` returns a `Reader` for the parameters, and `context.getResults()`
|
cannam@133
|
290 returns a `Builder` for the results. The context also has methods for controlling RPC logistics,
|
cannam@133
|
291 such as cancellation -- see `capnp::CallContext` in `capnp/capability.h` for details.
|
cannam@133
|
292
|
cannam@133
|
293 Accessing the results through the context (rather than by returning them) is unintuitive, but
|
cannam@133
|
294 necessary because the underlying RPC transport needs to have control over where the results are
|
cannam@133
|
295 allocated. For example, a zero-copy shared memory transport would need to allocate the results in
|
cannam@133
|
296 the shared memory segment. Hence, the method implementation cannot just create its own
|
cannam@133
|
297 `MessageBuilder`.
|
cannam@133
|
298
|
cannam@133
|
299 {% highlight c++ %}
|
cannam@133
|
300 class DirectoryImpl final: public Directory::Server {
|
cannam@133
|
301 public:
|
cannam@133
|
302 kj::Promise<void> open(OpenContext context) override {
|
cannam@133
|
303 auto iter = files.find(context.getParams().getName());
|
cannam@133
|
304
|
cannam@133
|
305 // Throw an exception if not found.
|
cannam@133
|
306 KJ_REQUIRE(iter != files.end(), "File not found.");
|
cannam@133
|
307
|
cannam@133
|
308 context.getResults().setFile(iter->second);
|
cannam@133
|
309
|
cannam@133
|
310 return kj::READY_NOW;
|
cannam@133
|
311 }
|
cannam@133
|
312
|
cannam@133
|
313 // Any method which we don't implement will simply throw
|
cannam@133
|
314 // an exception by default.
|
cannam@133
|
315
|
cannam@133
|
316 private:
|
cannam@133
|
317 std::map<kj::StringPtr, File::Client> files;
|
cannam@133
|
318 };
|
cannam@133
|
319 {% endhighlight %}
|
cannam@133
|
320
|
cannam@133
|
321 On the server side, [generic methods](language.html#generic-methods) are NOT templates. Instead,
|
cannam@133
|
322 the generated code is exactly as if all of the generic parameters were bound to `AnyPointer`. The
|
cannam@133
|
323 server generally does not get to know exactly what type the client requested; it must be designed
|
cannam@133
|
324 to be correct for any parameterization.
|
cannam@133
|
325
|
cannam@133
|
326 ## Initializing RPC
|
cannam@133
|
327
|
cannam@133
|
328 Cap'n Proto makes it easy to start up an RPC client or server using the "EZ RPC" classes,
|
cannam@133
|
329 defined in `capnp/ez-rpc.h`. These classes get you up and running quickly, but they hide a lot
|
cannam@133
|
330 of details that power users will likely want to manipulate. Check out the comments in `ez-rpc.h`
|
cannam@133
|
331 to understand exactly what you get and what you miss. For the purpose of this overview, we'll
|
cannam@133
|
332 show you how to use EZ RPC to get started.
|
cannam@133
|
333
|
cannam@133
|
334 ### Starting a client
|
cannam@133
|
335
|
cannam@133
|
336 A client should typically look like this:
|
cannam@133
|
337
|
cannam@133
|
338 {% highlight c++ %}
|
cannam@133
|
339 #include <capnp/ez-rpc.h>
|
cannam@133
|
340 #include "my-interface.capnp.h"
|
cannam@133
|
341 #include <iostream>
|
cannam@133
|
342
|
cannam@133
|
343 int main(int argc, const char* argv[]) {
|
cannam@133
|
344 // We expect one argument specifying the server address.
|
cannam@133
|
345 if (argc != 2) {
|
cannam@133
|
346 std::cerr << "usage: " << argv[0] << " HOST[:PORT]" << std::endl;
|
cannam@133
|
347 return 1;
|
cannam@133
|
348 }
|
cannam@133
|
349
|
cannam@133
|
350 // Set up the EzRpcClient, connecting to the server on port
|
cannam@133
|
351 // 5923 unless a different port was specified by the user.
|
cannam@133
|
352 capnp::EzRpcClient client(argv[1], 5923);
|
cannam@133
|
353 auto& waitScope = client.getWaitScope();
|
cannam@133
|
354
|
cannam@133
|
355 // Request the bootstrap capability from the server.
|
cannam@133
|
356 MyInterface::Client cap = client.getMain<MyInterface>();
|
cannam@133
|
357
|
cannam@133
|
358 // Make a call to the capability.
|
cannam@133
|
359 auto request = cap.fooRequest();
|
cannam@133
|
360 request.setParam(123);
|
cannam@133
|
361 auto promise = request.send();
|
cannam@133
|
362
|
cannam@133
|
363 // Wait for the result. This is the only line that blocks.
|
cannam@133
|
364 auto response = promise.wait(waitScope);
|
cannam@133
|
365
|
cannam@133
|
366 // All done.
|
cannam@133
|
367 std::cout << response.getResult() << std::endl;
|
cannam@133
|
368 return 0;
|
cannam@133
|
369 }
|
cannam@133
|
370 {% endhighlight %}
|
cannam@133
|
371
|
cannam@133
|
372 Note that for the connect address, Cap'n Proto supports DNS host names as well as IPv4 and IPv6
|
cannam@133
|
373 addresses. Additionally, a Unix domain socket can be specified as `unix:` followed by a path name.
|
cannam@133
|
374
|
cannam@133
|
375 For a more complete example, see the
|
cannam@133
|
376 [calculator client sample](https://github.com/sandstorm-io/capnproto/tree/master/c++/samples/calculator-client.c++).
|
cannam@133
|
377
|
cannam@133
|
378 ### Starting a server
|
cannam@133
|
379
|
cannam@133
|
380 A server might look something like this:
|
cannam@133
|
381
|
cannam@133
|
382 {% highlight c++ %}
|
cannam@133
|
383 #include <capnp/ez-rpc.h>
|
cannam@133
|
384 #include "my-interface-impl.h"
|
cannam@133
|
385 #include <iostream>
|
cannam@133
|
386
|
cannam@133
|
387 int main(int argc, const char* argv[]) {
|
cannam@133
|
388 // We expect one argument specifying the address to which
|
cannam@133
|
389 // to bind and accept connections.
|
cannam@133
|
390 if (argc != 2) {
|
cannam@133
|
391 std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]"
|
cannam@133
|
392 << std::endl;
|
cannam@133
|
393 return 1;
|
cannam@133
|
394 }
|
cannam@133
|
395
|
cannam@133
|
396 // Set up the EzRpcServer, binding to port 5923 unless a
|
cannam@133
|
397 // different port was specified by the user. Note that the
|
cannam@133
|
398 // first parameter here can be any "Client" object or anything
|
cannam@133
|
399 // that can implicitly cast to a "Client" object. You can even
|
cannam@133
|
400 // re-export a capability imported from another server.
|
cannam@133
|
401 capnp::EzRpcServer server(kj::heap<MyInterfaceImpl>(), argv[1], 5923);
|
cannam@133
|
402 auto& waitScope = server.getWaitScope();
|
cannam@133
|
403
|
cannam@133
|
404 // Run forever, accepting connections and handling requests.
|
cannam@133
|
405 kj::NEVER_DONE.wait(waitScope);
|
cannam@133
|
406 }
|
cannam@133
|
407 {% endhighlight %}
|
cannam@133
|
408
|
cannam@133
|
409 Note that for the bind address, Cap'n Proto supports DNS host names as well as IPv4 and IPv6
|
cannam@133
|
410 addresses. The special address `*` can be used to bind to the same port on all local IPv4 and
|
cannam@133
|
411 IPv6 interfaces. Additionally, a Unix domain socket can be specified as `unix:` followed by a
|
cannam@133
|
412 path name.
|
cannam@133
|
413
|
cannam@133
|
414 For a more complete example, see the
|
cannam@133
|
415 [calculator server sample](https://github.com/sandstorm-io/capnproto/tree/master/c++/samples/calculator-server.c++).
|
cannam@133
|
416
|
cannam@133
|
417 ## Debugging
|
cannam@133
|
418
|
cannam@133
|
419 If you've written a server and you want to connect to it to issue some calls for debugging, perhaps
|
cannam@133
|
420 interactively, the easiest way to do it is to use [pycapnp](http://jparyani.github.io/pycapnp/).
|
cannam@133
|
421 We have decided not to add RPC functionality to the `capnp` command-line tool because pycapnp is
|
cannam@133
|
422 better than anything we might provide.
|