Perl 6 udp socket:如何从服务器读取响应?

时间:2018-01-16 14:29:01

标签: sockets perl6

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!");

客户如何阅读服务器响应?
也许这还没有实现?

例如在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";
...

2 个答案:

答案 0 :(得分:4)

首先让我重申一下我的评论。从阅读IO::Socket::Async的文档,我没有看到明显的方法来做到这一点。您既可以设置UDP发送方,也可以设置UDP接收方,但不能同时设置两者。

UDP连接由4个东西定义,(发送方地址,发送方端口,接收方地址,接收方端口)。

服务器可以侦听给定的地址/端口。一旦收到数据包,通常有办法查询发件人的地址/端口。对于Perl 6来说,这是我没有看到的。

客户端可以将数据包定向到特定的服务器地址/端口。客户通常会选择一个随机的发件人端口,然后提供连接所需的第四个元素' (在这种无连接协议中)。

因此,正如在其他语言的示例中,客户端发送数据包,服务器查找发送方的地址/端口,然后将数据包返回到同一地址/端口。在发送其数据包之后,客户端再次侦听它发送数据包的相同随机端口,以接收来自服务器的响应。我没有在Perl 6中看到一个显而易见的方法,即在发送到同一端口的print-to上跟recv进行跟进。

有了这个说法,Perl 6有一个很棒的NativeCall工具可以用来直接调用动态库,所以如果你愿意的话,你可以用实际的系统调用完成所需的一切。

这不是官方的'以任何方式Perl 6方式,一旦IO::Socket::Async可以做你想做的事,就可以从你的大脑中清除所有这些,但是这里有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;

答案 1 :(得分:2)

在得到答案之前,除非您不关心从谁那里接收数据,否则您不是在以正确的UDP套接字方式监听服务器上的数据。您应该将:datagram传递给IO::Socket::Async.Supply,这样才能使Tappable返回的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-udpIO::Socket::Async.udp都返回一个IO::Socket::Async实例,因此您在客户端上侦听消息的方式与在服务器上相同:

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