-
Notifications
You must be signed in to change notification settings - Fork 292
/
Async.pod6
303 lines (226 loc) · 9.2 KB
/
Async.pod6
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
=begin pod
=TITLE class IO::Socket::Async
=SUBTITLE Asynchronous socket in TCP or UDP
class IO::Socket::Async {}
C<IO::Socket::Async> provides asynchronous sockets, for both the
server and the client side.
Here is a simple example of a simple "hello world" http server that
listens on port 3333:
=begin code :solo
react {
whenever IO::Socket::Async.listen('0.0.0.0', 3333) -> $conn {
whenever $conn.Supply.lines -> $line {
$conn.print: qq:heredoc/END/;
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Encoding: UTF-8
<html>
<body>
<h1>Hello World!</h1>
<p>{ $line }</p>
</body>
</html>
END
$conn.close;
}
}
CATCH {
default {
say .^name, ': ', .Str;
say "handled in $?LINE";
}
}
}
=end code
And a client that connects to it, and prints out what the server answers:
=begin code
await IO::Socket::Async.connect('127.0.0.1', 3333).then( -> $promise {
given $promise.result {
.print("Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n");
react {
whenever .Supply() -> $v {
$v.print;
done;
}
}
.close;
}
});
=end code
L<IO::Socket::Async> can send and receive
L<UDP|https://en.wikipedia.org/wiki/User_Datagram_Protocol> messages
An example server that outputs all the data it receives would be:
=begin code
my $socket = IO::Socket::Async.bind-udp('localhost', 3333);
react {
whenever $socket.Supply -> $v {
if $v.chars > 0 {
say $v;
}
}
}
=end code
And an associated client might be:
=begin code
my $socket = IO::Socket::Async.udp();
await $socket.print-to('localhost', 3333, "Hello, Perl 6!");
=end code
The L<C<CATCH> phaser|/language/phasers#CATCH> can be included to deal
specifically with problems that might occur in this kind of sockets, such as a
port being already taken:
=begin code
react {
whenever IO::Socket::Async.listen('0.0.0.0', 3000) -> $conn {
whenever $conn.Supply.lines -> $line {
$conn.print: qq:heredoc/END/;
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Encoding: UTF-8
<html>
<body>
<h1>Hello World!</h1>
<p>{ $line }</p>
</body>
</html>
END
$conn.close;
}
QUIT {
default {
say .^name, '→ ', .Str;
say "handled in line $?LINE";
}
}
}
}
# Will print this, if address 3000 is already in use:
# X::AdHoc→ address already in use
# handled in 23
=end code
Main difference with using other phasers such as C<CATCH> is that this kind of
exception will be caught within the C<whenever> block and will put exiting the
program, or not, under your control.
=head1 Methods
The L<IO::Socket::Async> cannot be constructed directly, either
C<connect> or C<listen> (for TCP connections) or C<udp> or C<bind-udp>
(for UDP data) should be used to create a client or a server
respectively.
=head2 method connect
method connect(Str $host, Int $port --> Promise)
Attempts to connect to the TCP server specified by C<$host> and
C<$port>, returning a L<Promise|/type/Promise> that will either be kept with a
connected L<IO::Socket::Async> or broken if the connection cannot be
made.
=head2 method listen
method listen(Str $host, Int $port --> Supply)
Creates a listening socket on the specified C<$host> and C<$port>,
returning a L<Supply|/type/Supply> to which the accepted client L<IO::Socket::Async>s
will be C<emit>ted. This L<Supply|/type/Supply> should be tapped start listening for
client connections.
To close the underlying listening socket, the L<Tap|/type/Tap> returned by tapping
the listener should be C<close>d.
For example, when using C<tap>:
=begin code
my $listener = IO::Socket::Async.listen('127.0.0.1', 8080);
my $tap = $listener.tap({ ... });
# When you want to close the listener
$tap.close;
=end code
Or when using C<whenever>:
=begin code
my $listener = IO::Socket::Async.listen('127.0.0.1', 5000);
my $tap;
react {
$tap = do whenever $listener -> $conn { ... }
}
# When you want to close the listener, you can still use:
$tap.close;
=end code
=head2 method udp
method udp(IO::Socket::Async:U: :$broadcast --> IO::Socket::Async)
Returns an initialised C<IO::Socket::Async> client object that is
configured to send UDP messages using C<print-to> or C<write-to>. The
C<:broadcast> adverb will set the C<SO_BROADCAST> option which will
allow the socket to send packets to a broadcast address.
=head2 method bind-udp
method bind-udp(IO::Socket::Async:U: Str() $host, Int() $port, :$broadcast --> IO::Socket::Async)
This returns an initialised C<IO::Socket::Async> server object that is
configured to receive UDP messages sent to the specified C<$host> and
C<$port> and is equivalent to C<listen> for a TCP socket. The
C<:broadcast> adverb can be specified to allow the receipt of messages
sent to the broadcast address.
=head2 method print
method print(Str $str --> Promise)
Attempt to send C<$str> on the L<IO::Socket::Async> that will have been
obtained indirectly via C<connect> or C<listen>, returning a L<Promise|/type/Promise>
that will be kept with the number of bytes sent or broken if there was
an error sending.
=head2 method print-to
method print-to(IO::Socket::Async:D: Str() $host, Int() $port, Str() $str --> Promise)
This is the equivalent of C<print> for UDP sockets that have been
created with the C<udp> method, it will try send a UDP message of
C<$str> to the specified C<$host> and C<$port> returning a
L<Promise|/type/Promise> that will be kept when the data is successfully
sent or broken if it was unable to send the data. In order to send to a
broadcast address the C<:broadcast> flag must have been specified when
the socket was created.
=head2 method write
method write(Blob $b --> Promise)
This method will attempt to send the bytes in C<$b> on the
L<IO::Socket::Async> that will have been obtained indirectly via
C<connect> or C<listen>, returning a L<Promise|/type/Promise> that will be kept with
the number of bytes sent or broken if there was an error sending.
=head2 method write-to
method write-to(IO::Socket::Async:D: Str() $host, Int() $port, Blob $b --> Promise)
This is the equivalent of C<write> for UDP sockets that have been
created with the C<udp> method. It will try send a UDP message comprised
of the bytes in the L<Blob|/type/Blob> C<$b> to the specified C<$host>
and C<$port> returning a L<Promise|/type/Promise> that will be kept when
the data is successfully sent or broken if it was unable to send the
data. In order to send to a broadcast address the C<:broadcast> flag
must have been specified when the socket was created.
=head2 method Supply
method Supply(:$bin, :$buf = buf8.new --> Supply)
Returns a L<Supply|/type/Supply> which can be tapped to obtain the data read from
the connected L<IO::Socket::Async> as it arrives. By default the data
will be emitted as characters, but if the C<:bin> adverb is provided a
L<Buf|/type/Buf> of bytes will be emitted instead, optionally in this
case you can provide your own C<Buf> with the C<:buf> named parameter.
A UDP socket in character mode will treat each packet as a complete
message and decode it. In the event of a decoding error, the C<Supply>
will C<quit>.
On the other hand, a TCP socket treats the incoming packets as part of a
stream, and feeds the incoming bytes into a streaming decoder. It then
emits whatever characters the decoder considers ready. Since strings
work at grapheme level in Perl 6, this means that only known complete
graphemes will be emitted. For example, if the UTF-8 encoding were
being used and the last byte in the packet decoded to C<a>, this would
not be emitted since the next packet may include a combining character
that should form a single grapheme together with the C<a>. Control
characters (such as C<\n>) always serve as grapheme boundaries, so any
text-based protocols that use newlines or null bytes as terminators
will not need special consideration. A TCP socket will also C<quit>
upon a decoding error.
=head2 method close
method close()
Close the connected client L<IO::Socket::Async> which will have been
obtained from the C<listen> L<Supply|/type/Supply> or the C<connect>
L<Promise|/type/Promise>.
In order to close the underlying listening socket created by C<listen> you
can C<close> the L<Tap|/type/Tap>. See C<listen> for examples.
=head2 method socket-host
method socket-host(--> Str)
Returns the IP address of the local end of this socket.
=head2 method peer-host
method peer-host(--> Str)
Returns the IP address of the remote end of this socket.
=head2 method socket-port
method socket-port(--> Int)
Returns the port of the local end of this socket.
=head2 method peer-port
method peer-port(--> Int)
Returns the port of the remote end of this socket.
=head2 method native-descriptor
method native-descriptor(--> Int)
Returns the file descriptor of this socket.
=end pod