class IO::Socket::Async {}

IO::Socket::Async 提供异步套接字,适用于服务器和客户端。

下面是一个简单的“hello world”HTTP 服务器示例,该服务器监听端口 3333

react {
    whenever IO::Socket::Async.listen('0.0.0.0'8080-> $conn {
        await $conn.print: qq:heredoc/END/; 
            HTTP/1.1 200 OK
            Content-Type: text/html; charset=UTF-8
            Content-Encoding: UTF-8
 
            <html>
            <body>
                <h1>Incoming request:</h1>
            <pre>
            END
        whenever $conn.Supply.lines -> $line {
            $conn.say: $line;
            if $line ~~ "" {
                await $conn.print: "</pre></body></html>";
                $conn.close;
            }
            LAST { say "closed connection"}
        }
    }
    CATCH {
        default {
            say .^name''.Str;
            say "handled in $?LINE";
        }
    }
}

以及一个连接到它的客户端,并打印出服务器的回答

await IO::Socket::Async.connect('127.0.0.1'3333).then-> $promise {
    given $promise.result {
        .print("Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n");
        react {
            whenever .Supply() -> $v {
                $v.print;
                done;
            }
        }
        .close;
    }
});

IO::Socket::Async 还可以发送和接收 UDP 消息。输出其接收的所有数据的示例服务器如下

my $socket = IO::Socket::Async.bind-udp('localhost'3333);
 
react {
    whenever $socket.Supply -> $v {
        if $v.chars > 0 {
            say $v;
        }
    }
}

关联的客户端可能是

my $socket = IO::Socket::Async.udp();
await $socket.print-to('localhost'3333"Hello, Raku!");

可以包含 CATCH phaser,以专门处理此类套接字中可能发生的错误,例如端口已被占用

react {
    whenever IO::Socket::Async.listen('0.0.0.0'3000-> $conn {
        whenever $conn.Supply.lines -> $line {
            $conn.print: qq:heredoc/END/; 
                HTTP/1.1 200 OK
                Content-Type: text/html; charset=UTF-8
                Content-Encoding: UTF-8
 
                <html>
                <body>
                    <h1>Hello World!</h1>
                    <p>{ $line }</p>
                </body>
                </html>
                END
            $conn.close;
        }
        QUIT {
            default {
                say .^name'→ '.Str;
                say "handled in line $?LINE";
            }
        }
    }
 
}
# Will print this, if address 3000 is already in use: 
# X::AdHoc→ address already in use 
# handled in 23 

与使用其他 phaser(例如 CATCH)的主要区别在于,此类异常将在 whenever 块中捕获,并将使程序退出(或不退出)受您的控制。

方法§

IO::Socket::Async 无法直接构造,应使用 connectlisten(用于 TCP 连接)或 udpbind-udp(用于 UDP 数据)来分别创建客户端或服务器。

方法 connect§

method connect(Str $hostInt $port --> Promise)

尝试连接到 $host$port 指定的 TCP 服务器,返回一个 Promise,该 Promise 将保留已连接的 IO::Socket::Async,或者在无法建立连接时中断。

方法 connect-path§

method connect-path(Str $path --> Promise)

尝试连接到 $path 指定的 Unix 域流套接字,返回一个 Promise,该 Promise 将保留已连接的 IO::Socket::Async,或者在无法建立连接时中断。

方法 listen§

method listen(Str $hostInt $port --> Supply)

在指定的 $host$port 上创建一个侦听套接字,返回一个 Supply,已接受的客户端 IO::Socket::Async 将被发送到该 Supply。应点按此 Supply 以开始侦听客户端连接。如果您希望操作系统为您查找端口,则可以将 $port 设置为 0

通过对返回的 Supply 调用 tap 方法返回的 IO::Socket::Async::ListenSocket 表示底层侦听 TCP 套接字,可以使用其 close 方法关闭该套接字。如果 $port 设置为 0,则可以使用其 socket-port 方法获取套接字最终使用的端口。

方法 listen-path§

method listen-path(Str $path)

在指定的 $path 上创建一个 Unix 域流监听套接字,返回一个 Supply,其中将发出已接受的客户端 IO::Socket::Async。此 Supply 应被轻触以开始监听客户端连接。

通过调用供应上返回的 tap 方法返回的 IO::Socket::Async::ListenSocket 表示底层监听 TCP 套接字,可以使用其 close 方法关闭该套接字。

方法 udp§

method udp(IO::Socket::Async:U: :$broadcast --> IO::Socket::Async)

返回一个已初始化的 IO::Socket::Async 客户端对象,该对象已配置为使用 print-towrite-to 发送 UDP 消息。:broadcast 副词将设置 SO_BROADCAST 选项,该选项将允许套接字向广播地址发送数据包。

方法 bind-udp§

method bind-udp(IO::Socket::Async:U: Str() $hostInt() $port:$broadcast --> IO::Socket::Async)

这将返回一个已初始化的 IO::Socket::Async 服务器对象,该对象已配置为接收发送到指定的 $host$port 的 UDP 消息,并且等效于 TCP 套接字的 listen。可以指定 :broadcast 副词以允许接收发送到广播地址的消息。

方法 print§

method print(IO::Socket::Async:D: Str $str --> Promise)

尝试在 IO::Socket::Async 上发送 $str,该 IO::Socket::Async 将通过 connectlisten 间接获取,返回一个 Promise,该 Promise 将保留发送的字节数或在发送时出错时中断。

方法 print-to§

method print-to(IO::Socket::Async:D: Str() $hostInt() $portStr() $str --> Promise)

这等效于使用 udp 方法创建的 UDP 套接字的 print,它将尝试向指定的 $host$port 发送 $str 的 UDP 消息,返回一个 Promise,该 Promise 将在成功发送数据时保留,或在无法发送数据时中断。为了发送到广播地址,必须在创建套接字时指定 :broadcast 标志。

方法 write§

method write(IO::Socket::Async:D: Blob $b --> Promise)

此方法将尝试在 IO::Socket::Async 上发送 $b 中的字节,该 IO::Socket::Async 将通过 connectlisten 间接获取,返回一个 Promise,该 Promise 将保留发送的字节数或在发送时出错时中断。

方法 write-to§

method write-to(IO::Socket::Async:D: Str() $hostInt() $portBlob $b --> Promise)

这等效于使用 udp 方法创建的 UDP 套接字的 write。它将尝试向指定的 $host$port 发送由 Blob $b 中的字节组成的 UDP 消息,返回一个 Promise,该 Promise 将在成功发送数据时保留,或在无法发送数据时中断。为了发送到广播地址,必须在创建套接字时指定 :broadcast 标志。

方法 Supply§

method Supply(:$bin:$buf = buf8.new --> Supply)

返回一个 Supply,可以点击它来获取从连接的 IO::Socket::Async 中读取的数据。默认情况下,数据将以字符形式发出,但如果提供了 :bin 副词,则会发出一个字节的 Buf,在这种情况下,您可以使用 :buf 命名参数提供自己的 Buf

字符模式下的 UDP 套接字将每个数据包视为一个完整的消息并对其进行解码。如果发生解码错误,则 Supplyquit

另一方面,TCP 套接字将传入的数据包视为流的一部分,并将传入的字节馈入流式解码器。然后它会发出解码器认为已准备好的任何字符。由于字符串在 Raku 中以音素级别工作,这意味着只会发出已知的完整音素。例如,如果使用 UTF-8 编码并且数据包中的最后一个字节解码为 a,则不会发出此字节,因为下一个数据包可能包含一个组合字符,该字符应与 a 形成一个音素。控制字符(例如 \n)始终作为音素边界,因此任何使用换行符或空字节作为终止符的基于文本的协议都不需要特殊考虑。TCP 套接字在解码错误时也会 quit

方法 close§

method close(IO::Socket::Async:D: )

关闭连接的客户端 IO::Socket::Async,该客户端将从 listen Supplyconnect Promise 中获取。

为了关闭 listen 创建的底层侦听套接字,您可以 close IO::Socket::Async::ListenSocket。有关示例,请参见 listen

方法 socket-host§

method socket-host(--> Str)

返回此套接字本地端的 IP 地址。

方法 peer-host§

method peer-host(--> Str)

返回此套接字远程端的 IP 地址。

方法 socket-port§

method socket-port(--> Int)

返回此套接字本地端的端口。

方法 peer-port§

method peer-port(--> Int)

返回此套接字远程端的端口。

方法 native-descriptor§

method native-descriptor(--> Int)

返回此套接字的文件描述符。