-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathipc.html
More file actions
156 lines (144 loc) · 7.64 KB
/
Copy pathipc.html
File metadata and controls
156 lines (144 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
---
layout: base
project: remoc
title: "IPC in Rust: typed channels and RPC between processes over a UNIX socket"
description: >-
How to build inter-process communication between a Rust daemon and its clients with
typed channels and trait calls over a UNIX socket, a pipe to a child process or a
local TCP connection, without a hand-rolled protocol or schema files.
social_description: >-
Inter-process communication for Rust with typed channels and trait calls over a UNIX
socket or pipe.
canonical: /ipc.html
faq:
- q: "Does this work on Windows?"
a: >-
Yes. Remoc runs over anything implementing <code>AsyncRead</code> and
<code>AsyncWrite</code>, which includes Tokio's named pipes on Windows. A TCP
connection on localhost works on every platform.
- q: "Can I talk to a child process I spawned?"
a: >-
Yes. The standard input and output pipes of a child process form a byte stream
that Remoc can run over. The transports module of the documentation contains a
worked example for this.
- q: "Can several clients connect to one daemon?"
a: >-
Yes. The daemon accepts connections on its socket as usual and establishes one
Remoc connection per client. Each client gets its own channels and trait clients,
isolated from the others.
- q: "What happens when daemon and client are different versions?"
a: >-
Remoc serializes with the Postbag codec, which supports schema evolution. Fields
and enum variants can be added, removed and reordered, so an updated daemon keeps
working with an already installed older client and vice versa.
- q: "How does this compare to D-Bus?"
a: >-
D-Bus is the right choice for talking to system services and desktop components,
since it is a shared bus with cross-language support. Remoc fits when both
processes are your own Rust code and you want your Rust types as the interface,
without a bus daemon in between.
- q: "Can a non-Rust process participate?"
a: >-
No. The protocol is defined by Rust types serialized with Serde, so both
processes must be written in Rust.
---
<header class="page-header">
{% include nav.html %}
<div class="wrap">
<h1>IPC between Rust processes</h1>
<p class="lead">
Many programs are really several processes that need to talk, for example a
daemon and its command-line client, a privileged helper and a user interface,
or a plugin running as a subprocess. <a href="/">Remoc</a> runs its typed
channels and trait calls over a UNIX socket, a pipe or any other local byte
stream.
</p>
</div>
</header>
<main id="content">
<section class="section">
<div class="wrap narrow">
<h2>Defining an IPC interface</h2>
<p>
The operating system gives you a byte stream for inter-process
communication, but what the processes actually need are commands with typed
arguments and results,
progress and event notifications, and an interface that can grow without
breaking installed clients.
</p>
<p>
A hand-rolled protocol over a UNIX socket means framing, a message enum and
dispatch code that grow with every command. JSON over stdin and stdout is
quick to start with, but untyped and without a versioning story. D-Bus
integrates well with system services, but brings a bus daemon and its own
type system. gRPC over a UNIX socket works, but maintaining a
<code>.proto</code> schema and generated code is a lot of machinery for two
programs that you compile yourself.
</p>
</div>
</section>
<section class="section alt">
<div class="wrap narrow">
<h2>Your Rust traits as the interface</h2>
<p>
With Remoc, the daemon's interface is a Rust trait carrying
<a href="https://docs.rs/remoc/latest/remoc/rtc/index.html"><code>#[rtc::remote]</code></a>,
which generates the client and server implementations. Arguments and results
are ordinary Serde types. For server-initiated messages, a method returns a
channel, for example a stream of log lines, progress values or state changes;
the <a href="/remote-channels.html">channels</a> work in both directions over
the same socket.
</p>
<p>
Since Remoc serializes with the <a href="/postbag/">Postbag</a> codec, the
interface can evolve. You can add fields, methods and enum variants and an
updated daemon keeps understanding an older installed client, without
version negotiation code.
</p>
<p>
The transport is whatever local byte stream suits you: a UNIX socket, the
stdin and stdout pipes of a child process, a named pipe on Windows or a TCP
connection on localhost. Remoc implements no transport itself, so nothing
else is pulled in.
</p>
</div>
</section>
<section class="section">
<div class="wrap">
<h2>Connecting to the daemon</h2>
<p class="section-lead">
The client connects to the daemon's socket and receives a typed client for
the daemon's interface trait. One call sets up the connection and delivers
it.
</p>
<figure class="code-single">
<figcaption>Client side</figcaption>
<pre><code><span class="k">use</span> tokio::net::<span class="t">UnixStream</span>;
<span class="k">let</span> socket = <span class="t">UnixStream</span>::connect(<span class="s">"/run/myapp.sock"</span>).<span class="k">await</span>?;
<span class="k">let</span> (socket_rx, socket_tx) = socket.into_split();
<span class="c">// Receive the client for the daemon's interface trait.</span>
<span class="k">let</span> <span class="k">mut</span> daemon: <span class="t">DaemonClient</span> =
remoc::<span class="t">Connect</span>::io(remoc::<span class="t">Cfg</span>::default(), socket_rx, socket_tx).consume().<span class="k">await</span>?;
daemon.status().<span class="k">await</span>?;</code></pre>
</figure>
<p class="section-foot">
<a href="https://docs.rs/remoc/latest/remoc/transports/index.html">Worked
transport examples, including pipes to a child process →</a>
</p>
</div>
</section>
{% include faq.html alt=true %}
<section class="section">
<div class="wrap narrow">
<h2>Related pages</h2>
<p>
The <a href="/examples/rtc.html">remote trait calling example</a> shows a
complete service with client and server crates. If the daemon needs to
notify its clients, <a href="/server-push.html">server push and
callbacks</a> covers the patterns, and
<a href="/remote-channels.html">channels between processes</a> explains the
underlying channel model.
</p>
</div>
</section>
</main>