Perl 6 udp 套接字:如何从服务器读取响应?
Perl 6 udp socket: how to read response from server?
服务器-udp.pl
my $socket = IO::Socket::Async.bind-udp('localhost', 3333);
react {
whenever $socket.Supply -> $v {
if $v.chars > 0 {
$v.print;
}
}
}
客户端-udp.pl
my $socket = IO::Socket::Async.udp();
await $socket.print-to('localhost', 3333, "\nHello, Perl 6!");
客户端如何读取服务器响应?
也许这还没有实现?
例如在 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";
...
首先让我重申一下我上面的评论。通过阅读 IO::Socket::Async 的文档,我没有看到一个明显的方法来做到这一点。您可以设置 UDP 发送方或 UDP 接收方,但不能同时设置两者。
UDP 连接由 4 项定义(发送方地址、发送方端口、接收方地址、接收方端口)。
服务器可以侦听给定的 address/port。收到数据包后,通常可以通过多种方式查询发件人的address/port。这是我在 Perl 6 中看不到的。
客户端可以将数据包定向到特定服务器 address/port。客户端通常会随机选择一个 'sender port',给出 'connection'(在此无连接协议中)所需的第四个元素。
因此,就像您在其他语言中的示例一样,客户端发送数据包,服务器查找发件人的 address/port,然后 returns 将数据包发送到相同的 address/port。客户端在发送完数据包后,再次监听发送数据包的同一个随机端口,以接收来自服务器的响应。我没有看到在 Perl 6 中有明显的方法可以在刚刚发送到的同一端口上用 recv
跟进 print-to
。
话虽如此,Perl 6 具有出色的 NativeCall 功能,可用于直接调用动态库,因此如果您愿意,可以使用实际的系统调用完成所需的一切。
这绝不是 'official' Perl 6 的方式,一旦 IO::Socket::Async
可以做你想做的事,就把这一切从你的大脑中清除掉,但这里是如何用 NativeCall
:
服务器-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;
}
客户端-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;
在我得到答案之前,除非您不关心您从谁那里接收数据,否则您不会以正确的方式在服务器上侦听 UDP 套接字的数据。您应该将 :datagram
传递给 IO::Socket::Async.Supply
,这样 Tappable
return 由 IO::Socket::Async.bind-udp
编辑会发出包含接收到的数据以及主机名和对等端口,而不是单独的数据:
my IO::Socket::Async::D $server .= bind-udp: 'localhost', 3333;
react whenever $server.Supply(:datagram) -> $datagram {
print $datagram.data if $datagram.data.chars > 0;
}
用于表示数据报的类型在编写时没有记录,但它只不过是一个容器,所以这就是它在 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))
}
}
除此之外,还有一种方法可以在不使用 NativeCall 的情况下使用 UDP 侦听客户端接收的数据; IO::Socket::Async.bind-udp
和 IO::Socket::Async.udp
都是 return 一个 IO::Socket::Async
实例,因此您可以像在服务器上一样在客户端上监听消息:
my IO::Socket::Async:D $client .= udp;
react whenever $client.Supply(:datagram) -> $datagram {
# ...
}
服务器-udp.pl
my $socket = IO::Socket::Async.bind-udp('localhost', 3333);
react {
whenever $socket.Supply -> $v {
if $v.chars > 0 {
$v.print;
}
}
}
客户端-udp.pl
my $socket = IO::Socket::Async.udp();
await $socket.print-to('localhost', 3333, "\nHello, Perl 6!");
客户端如何读取服务器响应?
也许这还没有实现?
例如在 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";
...
首先让我重申一下我上面的评论。通过阅读 IO::Socket::Async 的文档,我没有看到一个明显的方法来做到这一点。您可以设置 UDP 发送方或 UDP 接收方,但不能同时设置两者。
UDP 连接由 4 项定义(发送方地址、发送方端口、接收方地址、接收方端口)。
服务器可以侦听给定的 address/port。收到数据包后,通常可以通过多种方式查询发件人的address/port。这是我在 Perl 6 中看不到的。
客户端可以将数据包定向到特定服务器 address/port。客户端通常会随机选择一个 'sender port',给出 'connection'(在此无连接协议中)所需的第四个元素。
因此,就像您在其他语言中的示例一样,客户端发送数据包,服务器查找发件人的 address/port,然后 returns 将数据包发送到相同的 address/port。客户端在发送完数据包后,再次监听发送数据包的同一个随机端口,以接收来自服务器的响应。我没有看到在 Perl 6 中有明显的方法可以在刚刚发送到的同一端口上用 recv
跟进 print-to
。
话虽如此,Perl 6 具有出色的 NativeCall 功能,可用于直接调用动态库,因此如果您愿意,可以使用实际的系统调用完成所需的一切。
这绝不是 'official' Perl 6 的方式,一旦 IO::Socket::Async
可以做你想做的事,就把这一切从你的大脑中清除掉,但这里是如何用 NativeCall
:
服务器-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;
}
客户端-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;
在我得到答案之前,除非您不关心您从谁那里接收数据,否则您不会以正确的方式在服务器上侦听 UDP 套接字的数据。您应该将 :datagram
传递给 IO::Socket::Async.Supply
,这样 Tappable
return 由 IO::Socket::Async.bind-udp
编辑会发出包含接收到的数据以及主机名和对等端口,而不是单独的数据:
my IO::Socket::Async::D $server .= bind-udp: 'localhost', 3333;
react whenever $server.Supply(:datagram) -> $datagram {
print $datagram.data if $datagram.data.chars > 0;
}
用于表示数据报的类型在编写时没有记录,但它只不过是一个容器,所以这就是它在 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))
}
}
除此之外,还有一种方法可以在不使用 NativeCall 的情况下使用 UDP 侦听客户端接收的数据; IO::Socket::Async.bind-udp
和 IO::Socket::Async.udp
都是 return 一个 IO::Socket::Async
实例,因此您可以像在服务器上一样在客户端上监听消息:
my IO::Socket::Async:D $client .= udp;
react whenever $client.Supply(:datagram) -> $datagram {
# ...
}