server-udp.pl
my $socket = IO::Socket::Async.bind-udp('localhost', 3333);
react {
whenever $socket.Supply -> $v {
if $v.chars > 0 {
$v.print;
}
}
}
client-udp.pl
my $socket = IO::Socket::Async.udp();
await $socket.print-to('localhost', 3333, "\nHello, Perl 6!");
How can the client read the servers response?
Perhaps this is not yet implemented?
For example in Perl 5:
client.pl
...
my $data_send = "Test 1234567890";
$client_socket->send( $data_send )
or die "Client error while sending: $!\n";
# read operation
$client_socket->recv( my $data_rcv , 1024 )
or die "Client error while received: $!\n";
print "Received data: $data_rcv\n";
...
Before I get to the answer, unless you don't care about who you're receiving data from, you're not listening for data on the server the right way for UDP sockets. You should be passing :datagram
to IO::Socket::Async.Supply
, which makes it so the Tappable
returned by IO::Socket::Async.bind-udp
emits objects containing the data received as well as the hostname and port of the peer, rather than the data alone:
my IO::Socket::Async::D $server .= bind-udp: 'localhost', 3333;
react whenever $server.Supply(:datagram) -> $datagram {
print $datagram.data if $datagram.data.chars > 0;
}
The type used to represent datagrams is undocumented as of writing, but it's little more than a container, so this is how it is implemented in Rakudo:
my class Datagram {
has $.data;
has str $.hostname;
has int $.port;
method decode(|c) {
$!data ~~ Str
?? X::AdHoc.new( payload => "Cannot decode a datagram with Str data").throw
!! self.clone(data => $!data.decode(|c))
}
method encode(|c) {
$!data ~~ Blob
?? X::AdHoc.new( payload => "Cannot encode a datagram with Blob data" ).throw
!! self.clone(data => $!data.encode(|c))
}
}
With that out of the way, there is a way to listen for data received by clients using UDP without NativeCall; IO::Socket::Async.bind-udp
and IO::Socket::Async.udp
both return an IO::Socket::Async
instance, so you'd listen for messages on a client the same way you would for a server:
my IO::Socket::Async:D $client .= udp;
react whenever $client.Supply(:datagram) -> $datagram {
# ...
}
First let me reiterate my comment above. From reading the documentation for IO::Socket::Async, I don't see an obvious way to do this. You can either set up a UDP sender, or a UDP receiver, but not both.
UDP connections are defined by 4 things, (sender address, sender port, receiver address, receiver port).
A server can listen on a given address/port. Once a packet has been received, there are usually ways to query for the sender's address/port. That's what I don't see for Perl 6.
A client can direct a packet to a specific server address/port. The client usually picks a random 'sender port', giving the fourth element needed for a 'connection' (in this connection-less protocol).
So, as in your examples from other languages, the client sends the packet, the server looks up the sender's address/port, then returns a packet to that same address/port. The client, after sending off its packet, listens again to the same random port it sent the packet out on, to receive the response from the server. I don't see an obvious way in Perl 6 to follow up a print-to
with a recv
on the same port just sent to.
With that said, Perl 6 has a fantastic NativeCall facility that can be used to call dynamic libraries directly, so you can do everything you need with the actual system calls if you are so inclined.
This isn't the 'official' Perl 6 way by any means, and once IO::Socket::Async
can do what you want, purge all this from your brain, but here's how to do it with NativeCall
:
server-udp.pl
use NativeCall;
constant \AF_INET := 2;
constant \SOCK_DGRAM := 2;
class sockaddr_in is repr('CStruct')
{
has int16 $.sin_family;
has uint16 $.sin_port;
has int32 $.sin_addr;
has int64 $.pad;
}
sub socket(int32, int32, int32 --> int32) is native() {}
sub bind(int32, sockaddr_in, uint32 --> int32) is native() {}
sub htons(uint16 --> uint16) is native() {}
sub ntohs(uint16 --> uint16) is native() {}
sub inet_ntoa(int32 --> Str) is native() {}
sub perror(Str) is native() {}
sub recvfrom(int32, Blob, size_t, int32, sockaddr_in, int32 is rw --> ssize_t) is native() {}
sub sendto(int32, Blob, size_t, int32, sockaddr_in, int32 --> ssize_t) is native() {}
my int32 $sock = socket(AF_INET, SOCK_DGRAM, 0);
perror('socket') // die if $sock < 0;
my $addr = sockaddr_in.new(sin_family => AF_INET,
sin_port => htons(3333),
sin_addr => 0);
my $ret = bind($sock, $addr, nativesizeof(sockaddr_in));
perror('bind') // die if $ret < 0;
my $buf = buf8.allocate(1024);
my $fromaddr = sockaddr_in.new;
my int32 $addrsize = nativesizeof(sockaddr_in);
loop
{
$ret = recvfrom($sock, $buf, $buf.bytes, 0, $fromaddr, $addrsize);
perror('recvfrom') // die if $ret < 0;
my $msg = $buf.decode;
$msg.print;
my $return-msg = "Thank you for saying $msg";
my $return-buf = $return-msg.encode;
$ret = sendto($sock, $return-buf, $return-buf.bytes, 0, $fromaddr, $addrsize);
perror('sendto') // die if $ret < 0;
}
client-udp.pl
use NativeCall;
constant \AF_INET := 2;
constant \SOCK_DGRAM := 2;
class sockaddr_in is repr('CStruct')
{
has int16 $.sin_family;
has uint16 $.sin_port;
has int32 $.sin_addr;
has int64 $.pad;
}
sub socket(int32, int32, int32 --> int32) is native() {}
sub htons(uint16 --> uint16) is native() {}
sub inet_ntoa(int32 --> Str) is native() {}
sub inet_aton(Str, int32 is rw --> int32) is native() {}
sub perror(Str) is native() {}
sub recvfrom(int32, Blob, size_t, int32, sockaddr_in, int32 is rw --> ssize_t) is native() {}
sub recv(int32, Blob, size_t, int32 --> ssize_t) is native() {}
sub sendto(int32, Blob, size_t, int32, sockaddr_in, int32 --> ssize_t) is native() {}
my int32 $sock = socket(AF_INET, SOCK_DGRAM, 0);
perror('socket') // die if $sock < 0;
my int32 $addr-ip;
inet_aton('127.0.0.1', $addr-ip) or die "Bad address";
my $addr = sockaddr_in.new(sin_family => AF_INET,
sin_port => htons(3333),
sin_addr => $addr-ip);
my $msg = "Hello, Perl 6!\n".encode;
my $ret = sendto($sock, $msg, $msg.bytes, 0, $addr, nativesizeof(sockaddr_in));
perror('sendto') // die if $ret < 0;
my $buf = buf8.allocate(1024);
$ret = recv($sock, $buf, $buf.bytes, 0);
say "Return Msg: ", $buf.decode;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With