I can write a UDP server that will receive messages from a client using IO::Socket::Async
my $socket = IO::Socket::Async.bind-udp: 'localhost', 3333;
react whenever $socket.Supply: :bin {
say "Received some junk: { .gist }";
}
And I can send things to this server:
given IO::Socket::Async.udp {
await .print-to: 'localhost', 3333, 'Hello';
}
But when I try to get the server to send back a message response, I realise I don't have the host and port to send it to. How can I get this information?
Normally, I think the answer to this would be to use recvfrom
and pass a pointer to a sockaddr
struct that will get populated with the address information of the incoming connection. But there seems to be no way to do this without reaching for NativeCall (and even then it's not clear to me how to do that).
Or am I missing something?
TL;DR The solution to your problem was implemented by timo++ in response to a similar SO question to yours and then nicely documented by Kaiepi 🤍 in their accepted answer. Please visit and upvote there accordingly. The rest of what I write here is arguably redundant.
The official doc doesn't mention the solution (passing a :datagram
argument to the IO::Socket::Async.Supply
method) and using the Datagram
s you then get from the supply.
Here's an example of it using low level taps of supplies at both ends:
# Server
given IO::Socket::Async .bind-udp('localhost', 3333) {
.Supply(:datagram)
.tap: {
say "Received client data: { .data.gist }";
IO::Socket::Async.udp.print-to: .hostname, .port, 'Loud and clear!';
}
}
# Client
given IO::Socket::Async.udp {
.print-to: 'localhost', 3333, 'Can you hear me?';
.Supply
.tap: {
say "Received server data: { .gist }";
IO::Socket::Async.udp.print-to: 'localhost', 3333, 'Great :)';
}
}
sleep 1;
displays:
Received client data: Can you hear me?
Received server data: Hi!
Received client data: Great :)