Perl 6 udp socket: how to read response from server?

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 a client read the server 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"; ... 
+5
source share
2 answers

First let me repeat my comment above. From reading the documentation for IO :: Socket :: Async, I see no obvious way to do this. You can either configure the UDP sender or the UDP receiver, but not both.

UDP connections are determined by four things (sender address, sender port, receiver address, receiver port).

The server can listen to this address / port. Once a packet is received, there are usually ways to request the sender address / port. This is what I do not see for Perl 6.

The client can forward the packet to a specific server address / port. The client usually chooses a random "sender port", providing the fourth element necessary for the "connection" (in this protocol, without connection).

So, as in your examples from other languages, the client sends the packet, the server looks at the address / port of the sender, and then returns the packet to the same address / port. The client, having sent its packet, again listens to the same random port to which it sent the packet to receive a response from the server. I don't see an obvious way in Perl 6 to monitor print-to with recv in the same port just sent.

With that said, Perl 6 has a fantastic NativeCall object that you can use to directly invoke dynamic libraries, so you can do whatever you need with the actual system calls if you are so prone.

This is not the β€œofficial” Perl-6 method, and as soon as IO::Socket::Async can do what you want, clear all of 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; 
+4
source

Before I get a response, if you do not care about who you are receiving the data from, you are not listening to the data on the server in the right direction for UDP sockets. You must pass :datagram to IO::Socket::Async.Supply , so the Tappable returned by IO::Socket::Async.bind-udp emits objects containing the received data, as well as the host name and port of the peer, not data only:

 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 not documented at the time of writing, but it is slightly larger than the container, so 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)) } } 

There is a way to listen to data received by clients using UDP without NativeCall; IO::Socket::Async.bind-udp and IO::Socket::Async.udp both return an instance of IO::Socket::Async , so you will listen to messages on the client in the same way as on the server:

 my IO::Socket::Async:D $client .= udp; react whenever $client.Supply(:datagram) -> $datagram { # ... } 
+2
source

Source: https://habr.com/ru/post/1274734/


All Articles