ReactPHP 的异步、流式纯文本 TCP/IP 和安全 TLS 套接字服务器和客户端连接。
开发版本:此分支包含即将发布的 v3 版本的代码。有关当前稳定 v1 版本的代码,请查看
1.x
分支。即将发布的 v3 版本将是该软件包的前进方向。不过,我们仍然会积极支持尚未使用最新版本的 v1。另请参阅安装说明以了解更多详细信息。
套接字库为基于EventLoop
和Stream
组件的套接字层服务器和客户端提供可重用的接口。它的服务器组件允许您构建接受来自网络客户端(例如 HTTP 服务器)的传入连接的网络服务器。它的客户端组件允许您构建网络客户端,以建立到网络服务器的传出连接(例如 HTTP 或数据库客户端)。该库为所有这些提供了异步、流式传输方式,因此您可以处理多个并发连接而不会阻塞。
目录
这是一个服务器,如果您发送任何内容,它就会关闭连接:
$ socket = new React Socket SocketServer ( ' 127.0.0.1:8080 ' );
$ socket -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( " Hello " . $ connection -> getRemoteAddress () . " ! n" );
$ connection -> write ( " Welcome to this amazing server! n" );
$ connection -> write ( " Here's a tip: don't say anything. n" );
$ connection -> on ( ' data ' , function ( $ data ) use ( $ connection ) {
$ connection -> close ();
});
});
另请参阅示例。
这是一个客户端,它输出所述服务器的输出,然后尝试向其发送一个字符串:
$ connector = new React Socket Connector ();
$ connector -> connect ( ' 127.0.0.1:8080 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> pipe ( new React Stream WritableResourceStream ( STDOUT ));
$ connection -> write ( " Hello World! n" );
}, function ( Exception $ e ) {
echo ' Error: ' . $ e -> getMessage () . PHP_EOL ;
});
ConnectionInterface
用于表示任何传入和传出连接,例如普通的 TCP/IP 连接。
传入或传出连接是实现 React 的DuplexStreamInterface
的双工流(可读和可写)。它包含已建立此连接的本地和远程地址(客户端 IP)的附加属性。
最常见的是,实现此ConnectionInterface
实例由所有实现ServerInterface
类发出,并由所有实现ConnectorInterface
类使用。
因为ConnectionInterface
实现了底层DuplexStreamInterface
所以您可以照常使用它的任何事件和方法:
$ connection -> on ( ' data ' , function ( $ chunk ) {
echo $ chunk ;
});
$ connection -> on ( ' end ' , function () {
echo ' ended ' ;
});
$ connection -> on ( ' error ' , function ( Exception $ e ) {
echo ' error: ' . $ e -> getMessage ();
});
$ connection -> on ( ' close ' , function () {
echo ' closed ' ;
});
$ connection -> write ( $ data );
$ connection -> end ( $ data = null );
$ connection -> close ();
// …
有关更多详细信息,请参阅DuplexStreamInterface
。
getRemoteAddress(): ?string
方法返回建立此连接的完整远程地址 (URI)。
$ address = $ connection -> getRemoteAddress ();
echo ' Connection with ' . $ address . PHP_EOL ;
如果此时无法确定或未知远程地址(例如在连接关闭后),则它可以返回NULL
值。
否则,它将返回完整地址(URI)作为字符串值,例如tcp://127.0.0.1:8080
、 tcp://[::1]:80
、 tls://127.0.0.1:443
、 unix://example.sock
或unix:///path/to/example.sock
。请注意,各个 URI 组件是特定于应用程序的,并且取决于底层传输协议。
如果这是基于 TCP/IP 的连接,并且您只需要远程 IP,则可以使用如下内容:
$ address = $ connection -> getRemoteAddress ();
$ ip = trim ( parse_url ( $ address , PHP_URL_HOST ), ' [] ' );
echo ' Connection with ' . $ ip . PHP_EOL ;
getLocalAddress(): ?string
方法返回建立此连接的完整本地地址 (URI)。
$ address = $ connection -> getLocalAddress ();
echo ' Connection with ' . $ address . PHP_EOL ;
如果此时无法确定或未知本地地址(例如在连接关闭后),则它可以返回NULL
值。
否则,它将返回完整地址(URI)作为字符串值,例如tcp://127.0.0.1:8080
、 tcp://[::1]:80
、 tls://127.0.0.1:443
、 unix://example.sock
或unix:///path/to/example.sock
。请注意,各个 URI 组件是特定于应用程序的,并且取决于底层传输协议。
此方法补充了getRemoteAddress()
方法,因此不应将它们混淆。
如果您的TcpServer
实例正在侦听多个接口(例如使用地址0.0.0.0
),您可以使用此方法来找出哪个接口实际接受此连接(例如公共或本地接口)。
如果您的系统有多个接口(例如 WAN 和 LAN 接口),您可以使用此方法找出实际用于此连接的接口。
ServerInterface
负责提供接受传入流连接的接口,例如普通的 TCP/IP 连接。
大多数高级组件(例如 HTTP 服务器)接受实现此接口的实例来接受传入的流连接。这通常是通过依赖注入完成的,因此实际上将此实现与此接口的任何其他实现进行交换是相当简单的。这意味着您应该针对此接口进行类型提示,而不是此接口的具体实现。
除了定义一些方法之外,该接口还实现了EventEmitterInterface
,它允许您对某些事件做出反应。
每当建立新连接时就会发出connection
事件,即新客户端连接到该服务器套接字:
$ socket -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
echo ' new connection ' . PHP_EOL ;
});
另请参阅ConnectionInterface
以获取有关处理传入连接的更多详细信息。
每当接受来自客户端的新连接时出现错误时,就会发出error
事件。
$ socket -> on ( ' error ' , function ( Exception $ e ) {
echo ' error: ' . $ e -> getMessage () . PHP_EOL ;
});
请注意,这不是致命错误事件,即即使在该事件发生后,服务器仍会继续侦听新连接。
getAddress(): ?string
方法可用于返回该服务器当前正在侦听的完整地址 (URI)。
$ address = $ socket -> getAddress ();
echo ' Server listening on ' . $ address . PHP_EOL ;
如果此时无法确定或未知地址(例如在套接字关闭后),则它可能会返回NULL
值。
否则,它将返回完整地址(URI)作为字符串值,例如tcp://127.0.0.1:8080
、 tcp://[::1]:80
、 tls://127.0.0.1:443
unix://example.sock
或unix:///path/to/example.sock
。请注意,各个 URI 组件是特定于应用程序的,并且取决于底层传输协议。
如果这是基于 TCP/IP 的服务器并且您只需要本地端口,则可以使用如下内容:
$ address = $ socket -> getAddress ();
$ port = parse_url ( $ address , PHP_URL_PORT );
echo ' Server listening on port ' . $ port . PHP_EOL ;
该pause(): void
方法可用于暂停接受新的传入连接。
从 EventLoop 中删除套接字资源,从而停止接受新连接。请注意,侦听套接字保持活动状态并且未关闭。
这意味着新的传入连接将在操作系统积压中保持待处理状态,直到其可配置积压被填满。一旦积压被填满,操作系统可能会拒绝进一步的传入连接,直到通过恢复接受新连接而再次耗尽积压。
一旦服务器暂停,就不应再发出任何connection
事件。
$ socket -> pause ();
$ socket -> on ( ' connection ' , assertShouldNeverCalled ());
此方法仅供参考,尽管通常不推荐,但服务器可以继续发出connection
事件。
除非另有说明,成功打开的服务器不应以暂停状态启动。
您可以通过再次调用resume()
来继续处理事件。
请注意,这两种方法都可以调用任意多次,特别是多次调用pause()
不应产生任何效果。类似地,在close()
之后调用它也是一个 NO-OP。
resume(): void
方法可用于恢复接受新的传入连接。
在之前的pause()
之后将套接字资源重新附加到EventLoop。
$ socket -> pause ();
Loop:: addTimer ( 1.0 , function () use ( $ socket ) {
$ socket -> resume ();
});
请注意,这两种方法都可以调用任意多次,特别是在没有事先pause()
的情况下调用resume()
不应产生任何效果。类似地,在close()
之后调用它也是一个 NO-OP。
close(): void
方法可用于关闭此监听套接字。
这将停止侦听此套接字上的新传入连接。
echo ' Shutting down server socket ' . PHP_EOL ;
$ socket -> close ();
在同一个实例上多次调用此方法是 NO-OP。
SocketServer
类是此包中的主类,它实现ServerInterface
并允许您接受传入的流连接,例如纯文本 TCP/IP 或安全 TLS 连接流。
为了接受纯文本 TCP/IP 连接,您可以简单地传递主机和端口组合,如下所示:
$ socket = new React Socket SocketServer ( ' 127.0.0.1:8080 ' );
侦听本地主机地址127.0.0.1
意味着无法从该系统外部访问它。为了更改套接字正在侦听的主机,您可以提供接口的 IP 地址或使用特殊的0.0.0.0
地址来侦听所有接口:
$ socket = new React Socket SocketServer ( ' 0.0.0.0:8080 ' );
如果您想监听 IPv6 地址,则必须将主机括在方括号中:
$ socket = new React Socket SocketServer ( ' [::1]:8080 ' );
为了使用随机端口分配,您可以使用端口0
:
$ socket = new React Socket SocketServer ( ' 127.0.0.1:0 ' );
$ address = $ socket -> getAddress ();
要侦听 Unix 域套接字 (UDS) 路径,您必须在 URI 前面加上unix://
方案:
$ socket = new React Socket SocketServer ( ' unix:///tmp/server.sock ' );
为了监听现有的文件描述符 (FD) 编号,您必须在 URI 前面加上php://fd/
前缀,如下所示:
$ socket = new React Socket SocketServer ( ' php://fd/3 ' );
如果给定的 URI 无效、不包含端口、任何其他方案或者包含主机名,它将抛出InvalidArgumentException
:
// throws InvalidArgumentException due to missing port
$ socket = new React Socket SocketServer ( ' 127.0.0.1 ' );
如果给定的 URI 看起来有效,但监听失败(例如端口已在使用中或低于 1024 的端口可能需要 root 访问权限等),它将抛出RuntimeException
:
$ first = new React Socket SocketServer ( ' 127.0.0.1:8080 ' );
// throws RuntimeException because port is already in use
$ second = new React Socket SocketServer ( ' 127.0.0.1:8080 ' );
请注意,这些错误情况可能会因您的系统和/或配置而异。有关实际错误情况的更多详细信息,请参阅异常消息和代码。
或者,您可以为底层流套接字资源指定 TCP 套接字上下文选项,如下所示:
$ socket = new React Socket SocketServer ( ' [::1]:8080 ' , [
' tcp ' => [
' backlog ' => 200 ,
' so_reuseport ' => true ,
' ipv6_v6only ' => true
]
]);
请注意,可用的套接字上下文选项、其默认值以及更改这些选项的效果可能会有所不同,具体取决于您的系统和/或 PHP 版本。传递未知的上下文选项没有任何效果。除非明确指定,否则
backlog
上下文选项默认为511
。
您只需在前面添加tls://
URI 方案即可启动安全 TLS(以前称为 SSL)服务器。在内部,它将等待纯文本 TCP/IP 连接,然后为每个连接执行 TLS 握手。因此,它需要有效的 TLS 上下文选项,如果您使用 PEM 编码的证书文件,其最基本的形式可能如下所示:
$ socket = new React Socket SocketServer ( ' tls://127.0.0.1:8080 ' , [
' tls ' => [
' local_cert ' => ' server.pem '
]
]);
请注意,证书文件不会在实例化时加载,而是在传入连接初始化其 TLS 上下文时加载。这意味着任何无效的证书文件路径或内容只会在以后导致
error
事件。
如果您的私钥使用密码加密,则必须按如下方式指定:
$ socket = new React Socket SocketServer ( ' tls://127.0.0.1:8000 ' , [
' tls ' => [
' local_cert ' => ' server.pem ' ,
' passphrase ' => ' secret '
]
]);
默认情况下,此服务器支持 TLSv1.0+,但不支持旧版 SSLv2/SSLv3。您还可以明确选择要与远程端协商的 TLS 版本:
$ socket = new React Socket SocketServer ( ' tls://127.0.0.1:8000 ' , [
' tls ' => [
' local_cert ' => ' server.pem ' ,
' crypto_method ' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
]
]);
请注意,可用的 TLS 上下文选项、其默认值以及更改这些选项的效果可能会有所不同,具体取决于您的系统和/或 PHP 版本。外部上下文数组允许您同时使用
tcp
(可能还有更多)上下文选项。传递未知的上下文选项没有任何效果。如果不使用tls://
方案,则传递tls
上下文选项无效。
每当客户端连接时,它都会发出一个带有实现ConnectionInterface
的连接实例的connection
事件:
$ socket -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
echo ' Plaintext connection from ' . $ connection -> getRemoteAddress () . PHP_EOL ;
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
另请参阅ServerInterface
以获取更多详细信息。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
请注意,
SocketServer
类是 TCP/IP 套接字的具体实现。如果您想在更高级别的协议实现中输入提示,您应该使用通用的ServerInterface
。
TcpServer
类实现ServerInterface
并负责接受纯文本 TCP/IP 连接。
$ server = new React Socket TcpServer ( 8080 );
如上所述, $uri
参数只能包含一个端口,在这种情况下,服务器将默认侦听本地主机地址127.0.0.1
,这意味着从系统外部无法访问它。
为了使用随机端口分配,您可以使用端口0
:
$ server = new React Socket TcpServer ( 0 );
$ address = $ server -> getAddress ();
为了更改套接字正在侦听的主机,您可以通过提供给构造函数的第一个参数提供 IP 地址,可以选择在前面加上tcp://
方案:
$ server = new React Socket TcpServer ( ' 192.168.0.1:8080 ' );
如果您想监听 IPv6 地址,则必须将主机括在方括号中:
$ server = new React Socket TcpServer ( ' [::1]:8080 ' );
如果给定的 URI 无效、不包含端口、任何其他方案或者包含主机名,它将抛出InvalidArgumentException
:
// throws InvalidArgumentException due to missing port
$ server = new React Socket TcpServer ( ' 127.0.0.1 ' );
如果给定的 URI 看起来有效,但监听失败(例如端口已在使用中或低于 1024 的端口可能需要 root 访问权限等),它将抛出RuntimeException
:
$ first = new React Socket TcpServer ( 8080 );
// throws RuntimeException because port is already in use
$ second = new React Socket TcpServer ( 8080 );
请注意,这些错误情况可能会因您的系统和/或配置而异。有关实际错误情况的更多详细信息,请参阅异常消息和代码。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
或者,您可以为底层流套接字资源指定套接字上下文选项,如下所示:
$ server = new React Socket TcpServer ( ' [::1]:8080 ' , null , [
' backlog ' => 200 ,
' so_reuseport ' => true ,
' ipv6_v6only ' => true
]);
请注意,可用的套接字上下文选项、其默认值以及更改这些选项的效果可能会有所不同,具体取决于您的系统和/或 PHP 版本。传递未知的上下文选项没有任何效果。除非明确指定,否则
backlog
上下文选项默认为511
。
每当客户端连接时,它都会发出一个带有实现ConnectionInterface
的连接实例的connection
事件:
$ server -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
echo ' Plaintext connection from ' . $ connection -> getRemoteAddress () . PHP_EOL ;
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
另请参阅ServerInterface
以获取更多详细信息。
SecureServer
类实现ServerInterface
并负责提供安全 TLS(以前称为 SSL)服务器。
它通过包装一个TcpServer
实例来实现这一点,该实例等待纯文本 TCP/IP 连接,然后为每个连接执行 TLS 握手。因此,它需要有效的 TLS 上下文选项,如果您使用 PEM 编码的证书文件,其最基本的形式可能如下所示:
$ server = new React Socket TcpServer ( 8000 );
$ server = new React Socket SecureServer ( $ server , null , [
' local_cert ' => ' server.pem '
]);
请注意,证书文件不会在实例化时加载,而是在传入连接初始化其 TLS 上下文时加载。这意味着任何无效的证书文件路径或内容只会在以后导致
error
事件。
如果您的私钥使用密码加密,则必须按如下方式指定:
$ server = new React Socket TcpServer ( 8000 );
$ server = new React Socket SecureServer ( $ server , null , [
' local_cert ' => ' server.pem ' ,
' passphrase ' => ' secret '
]);
默认情况下,此服务器支持 TLSv1.0+,但不支持旧版 SSLv2/SSLv3。您还可以明确选择要与远程端协商的 TLS 版本:
$ server = new React Socket TcpServer ( 8000 );
$ server = new React Socket SecureServer ( $ server , null , [
' local_cert ' => ' server.pem ' ,
' crypto_method ' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
]);
请注意,可用的 TLS 上下文选项、其默认值以及更改这些选项的效果可能会有所不同,具体取决于您的系统和/或 PHP 版本。传递未知的上下文选项没有任何效果。
每当客户端完成 TLS 握手时,它将发出一个connection
事件,其中的连接实例实现ConnectionInterface
:
$ server -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
echo ' Secure connection from ' . $ connection -> getRemoteAddress () . PHP_EOL ;
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
每当客户端未能成功执行 TLS 握手时,它将发出error
事件,然后关闭底层 TCP/IP 连接:
$ server -> on ( ' error ' , function ( Exception $ e ) {
echo ' Error ' . $ e -> getMessage () . PHP_EOL ;
});
另请参阅ServerInterface
以获取更多详细信息。
请注意, SecureServer
类是 TLS 套接字的具体实现。如果您想在更高级别的协议实现中输入提示,您应该使用通用的ServerInterface
。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
高级用法:尽管允许任何
ServerInterface
作为第一个参数,但您应该传递TcpServer
实例作为第一个参数,除非您知道自己在做什么。在内部,SecureServer
必须在底层流资源上设置所需的 TLS 上下文选项。这些资源不通过此包中定义的任何接口公开,而仅通过内部Connection
类公开。TcpServer
类保证发出实现ConnectionInterface
的连接,并使用内部Connection
类来公开这些底层资源。如果您使用自定义ServerInterface
并且其connection
事件不满足此要求,SecureServer
将发出error
事件,然后关闭底层连接。
UnixServer
类实现ServerInterface
并负责接受 Unix 域套接字 (UDS) 上的连接。
$ server = new React Socket UnixServer ( ' /tmp/server.sock ' );
如上所述, $uri
参数只能包含套接字路径或以unix://
方案为前缀的套接字路径。
如果给定的 URI 看起来有效,但监听失败(例如套接字已在使用中或文件不可访问等),它将抛出RuntimeException
:
$ first = new React Socket UnixServer ( ' /tmp/same.sock ' );
// throws RuntimeException because socket is already in use
$ second = new React Socket UnixServer ( ' /tmp/same.sock ' );
请注意,这些错误情况可能会因您的系统和/或配置而异。特别是,当 UDS 路径已经存在且无法绑定时,Zend PHP 只会报告“未知错误”。在这种情况下,您可能需要检查给定 UDS 路径上的
is_file()
以报告更用户友好的错误消息。有关实际错误情况的更多详细信息,请参阅异常消息和代码。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
每当客户端连接时,它都会发出一个带有实现ConnectionInterface
的连接实例的connection
事件:
$ server -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
echo ' New connection ' . PHP_EOL ;
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
另请参阅ServerInterface
以获取更多详细信息。
LimitingServer
装饰器包装给定的ServerInterface
并负责限制和跟踪与此服务器实例的打开连接。
每当底层服务器发出connection
事件时,它将检查其限制,然后
connection
事件error
事件。每当连接关闭时,它都会从打开的连接列表中删除该连接。
$ server = new React Socket LimitingServer ( $ server , 100 );
$ server -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
有关更多详细信息,另请参阅第二个示例。
您必须传递最大打开连接数,以确保一旦超过此限制,服务器将自动拒绝(关闭)连接。在这种情况下,它将发出一个error
事件来通知这一点,并且不会发出任何connection
事件。
$ server = new React Socket LimitingServer ( $ server , 100 );
$ server -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
您可以传递null
限制,以便对打开连接的数量不加限制,并继续接受新连接,直到耗尽操作系统资源(例如打开的文件句柄)。如果您不想应用限制但仍想使用getConnections()
方法,这可能很有用。
您可以选择将服务器配置为在达到连接限制后暂停接受新连接。在这种情况下,它将暂停底层服务器并不再处理任何新连接,因此也不再关闭任何过多的连接。底层操作系统负责保留待处理连接的积压,直到达到其限制,此时它将开始拒绝进一步的连接。一旦服务器低于连接限制,它将继续消耗积压的连接,并处理每个连接上的任何未完成的数据。此模式对于某些设计用于等待响应消息(例如 HTTP)的协议可能很有用,但对于需要立即响应的其他协议(例如交互式聊天中的“欢迎”消息)可能不太有用。
$ server = new React Socket LimitingServer ( $ server , 100 , true );
$ server -> on ( ' connection ' , function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' hello there! ' . PHP_EOL );
…
});
getConnections(): ConnectionInterface[]
方法可用于返回包含所有当前活动连接的数组。
foreach ( $ server -> getConnection () as $ connection ) {
$ connection -> write ( ' Hi! ' );
}
ConnectorInterface
负责提供建立流连接的接口,例如普通的 TCP/IP 连接。
这是该包中定义的主要接口,在 React 庞大的生态系统中使用。
大多数高级组件(例如 HTTP、数据库或其他网络服务客户端)接受实现此接口的实例来创建与底层网络服务的 TCP/IP 连接。这通常是通过依赖注入完成的,因此实际上将此实现与此接口的任何其他实现交换是相当简单的。
该接口仅提供一个方法:
connect(string $uri): PromiseInterface<ConnectionInterface>
方法可用于创建到给定远程地址的流连接。
它返回一个 Promise,该 Promise 要么在成功时通过实现ConnectionInterface
流来实现,要么在连接不成功时以Exception
拒绝:
$ connector -> connect ( ' google.com:443 ' )-> then (
function ( React Socket ConnectionInterface $ connection ) {
// connection successfully established
},
function ( Exception $ error ) {
// failed to connect due to $error
}
);
另请参阅ConnectionInterface
了解更多详细信息。
返回的 Promise 必须以这样的方式实现:当它仍然处于挂起状态时可以将其取消。取消待处理的 Promise 必须使用Exception
拒绝其值。它应该清理任何适用的底层资源和引用:
$ promise = $ connector -> connect ( $ uri );
$ promise -> cancel ();
Connector
类是此包中的主类,它实现ConnectorInterface
并允许您创建流连接。
您可以使用此连接器创建任何类型的流连接,例如纯文本 TCP/IP、安全 TLS 或本地 Unix 连接流。
它绑定到主事件循环,可以像这样使用:
$ connector = new React Socket Connector ();
$ connector -> connect ( $ uri )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
}, function ( Exception $ e ) {
echo ' Error: ' . $ e -> getMessage () . PHP_EOL ;
});
为了创建纯文本 TCP/IP 连接,您可以简单地传递主机和端口组合,如下所示:
$ connector -> connect ( ' www.google.com:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
如果您没有在目标 URI 中指定 URI 方案,它将采用
tcp://
作为默认值并建立明文 TCP/IP 连接。请注意,TCP/IP 连接需要目标 URI 中的主机和端口部分(如上所示),所有其他 URI 组件都是可选的。
为了创建安全的 TLS 连接,您可以使用tls://
URI 方案,如下所示:
$ connector -> connect ( ' tls://www.google.com:443 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
为了创建本地 Unix 域套接字连接,您可以使用unix://
URI 方案,如下所示:
$ connector -> connect ( ' unix:///tmp/demo.sock ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
getRemoteAddress()
方法将返回给connect()
方法的目标 Unix 域套接字 (UDS) 路径,包括unix://
方案,例如unix:///tmp/demo.sock
。getLocalAddress()
方法很可能会返回null
值,因为该值不适用于此处的 UDS 连接。
在底层, Connector
被实现为该包中实现的较低级别连接器的较高级别外观。这意味着它也共享它们的所有功能和实现细节。如果您想在更高级别的协议实现中输入提示,则应该使用通用的ConnectorInterface
。
从v1.4.0
开始, Connector
类默认使用 happy eyeballs 算法在给定主机名时通过 IPv4 或 IPv6 自动连接。这会自动尝试同时使用 IPv4 和 IPv6 进行连接(首选 IPv6),从而避免了 IPv6 连接或设置不完善的用户所面临的常见问题。如果您想恢复到仅进行 IPv4 查找并仅尝试单个 IPv4 连接的旧行为,您可以像这样设置Connector
:
$ connector = new React Socket Connector ([
' happy_eyeballs ' => false
]);
同样,您也可以按如下方式影响默认 DNS 行为。 Connector
类将尝试检测您的系统 DNS 设置(如果无法确定您的系统设置,则使用 Google 的公共 DNS 服务器8.8.8.8
作为后备),以默认将所有公共主机名解析为底层 IP 地址。如果您明确想要使用自定义 DNS 服务器(例如本地 DNS 中继或公司范围的 DNS 服务器),您可以按如下方式设置Connector
:
$ connector = new React Socket Connector ([
' dns ' => ' 127.0.1.1 '
]);
$ connector -> connect ( ' localhost:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
如果您根本不想使用 DNS 解析器而只想连接到 IP 地址,您还可以像这样设置Connector
:
$ connector = new React Socket Connector ([
' dns ' => false
]);
$ connector -> connect ( ' 127.0.0.1:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
高级:如果您需要自定义 DNS ReactDnsResolverResolverInterface
实例,您还可以像这样设置Connector
:
$ dnsResolverFactory = new React Dns Resolver Factory ();
$ resolver = $ dnsResolverFactory -> createCached ( ' 127.0.1.1 ' );
$ connector = new React Socket Connector ([
' dns ' => $ resolver
]);
$ connector -> connect ( ' localhost:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
默认情况下, tcp://
和tls://
URI 方案将使用符合default_socket_timeout
ini 设置的超时值(默认为 60 秒)。如果您想要自定义超时值,您可以简单地像这样传递:
$ connector = new React Socket Connector ([
' timeout ' => 10.0
]);
同样,如果您根本不想应用超时并让操作系统处理此问题,您可以传递一个布尔标志,如下所示:
$ connector = new React Socket Connector ([
' timeout ' => false
]);
默认情况下, Connector
支持tcp://
、 tls://
和unix://
URI 方案。如果您想明确禁止其中任何一个,您可以简单地传递布尔标志,如下所示:
// only allow secure TLS connections
$ connector = new React Socket Connector ([
' tcp ' => false ,
' tls ' => true ,
' unix ' => false ,
));
$ connector -> connect ( ' tls://google.com:443 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
tcp://
和tls://
还接受传递给底层连接器的其他上下文选项。如果您想显式传递其他上下文选项,您可以简单地传递上下文选项数组,如下所示:
// allow insecure TLS connections
$ connector = new React Socket Connector ([
' tcp ' => [
' bindto ' => ' 192.168.0.1:0 '
],
' tls ' => [
' verify_peer ' => false ,
' verify_peer_name ' => false
],
]);
$ connector -> connect ( ' tls://localhost:443 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
默认情况下,此连接器支持 TLSv1.0+,但不支持旧版 SSLv2/SSLv3。您还可以明确选择要与远程端协商的 TLS 版本:
$ connector = new React Socket Connector ([
' tls ' => [
' crypto_method ' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
]
]);
有关上下文选项的更多详细信息,请参阅有关套接字上下文选项和 SSL 上下文选项的 PHP 文档。
高级:默认情况下, Connector
支持tcp://
、 tls://
和unix://
URI 方案。为此,它会自动设置所需的连接器类。如果您想显式传递其中任何一个的自定义连接器,您可以简单地传递一个实现ConnectorInterface
实例,如下所示:
$ dnsResolverFactory = new React Dns Resolver Factory ();
$ resolver = $ dnsResolverFactory -> createCached ( ' 127.0.1.1 ' );
$ tcp = new React Socket HappyEyeBallsConnector ( null , new React Socket TcpConnector (), $ resolver );
$ tls = new React Socket SecureConnector ( $ tcp );
$ unix = new React Socket UnixConnector ();
$ connector = new React Socket Connector ([
' tcp ' => $ tcp ,
' tls ' => $ tls ,
' unix ' => $ unix ,
' dns ' => false ,
' timeout ' => false ,
]);
$ connector -> connect ( ' google.com:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
在内部,
tcp://
连接器将始终由 DNS 解析器封装,除非您像上面的示例中那样禁用 DNS。在这种情况下,tcp://
连接器接收实际的主机名,而不仅仅是解析的 IP 地址,因此负责执行查找。在内部,自动创建的tls://
连接器将始终包装底层tcp://
连接器,以便在启用安全 TLS 模式之前建立底层明文 TCP/IP 连接。如果您只想将自定义底层tcp://
连接器用于安全 TLS 连接,则可以像上面那样显式传递tls://
连接器。在内部,tcp://
和tls://
连接器将始终由TimeoutConnector
包装,除非您像上面的示例一样禁用超时。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
TcpConnector
类实现ConnectorInterface
并允许您创建到任何 IP 端口组合的纯文本 TCP/IP 连接:
$ tcpConnector = new React Socket TcpConnector ();
$ tcpConnector -> connect ( ' 127.0.0.1:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
另请参阅示例。
可以通过取消其挂起的承诺来取消挂起的连接尝试,如下所示:
$ promise = $ tcpConnector -> connect ( ' 127.0.0.1:80 ' );
$ promise -> cancel ();
对待处理的 Promise 调用cancel()
将关闭底层套接字资源,从而取消待处理的 TCP/IP 连接,并拒绝生成的 Promise。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
您可以选择将其他套接字上下文选项传递给构造函数,如下所示:
$ tcpConnector = new React Socket TcpConnector ( null , [
' bindto ' => ' 192.168.0.1:0 '
]);
请注意,此类仅允许您连接到 IP 端口组合。如果给定的 URI 无效、不包含有效的 IP 地址和端口或包含任何其他方案,它将拒绝并抛出InvalidArgumentException
:
如果给定的 URI 看起来有效,但连接失败(例如远程主机拒绝连接等),它将拒绝并抛出RuntimeException
。
如果您想连接到主机名-端口组合,另请参阅下一章。
高级用法:在内部,
TcpConnector
为每个流资源分配一个空的上下文资源。如果目标 URI 包含hostname
查询参数,则其值将用于设置 TLS 对等名称。SecureConnector
和DnsConnector
使用它来验证对等名称,如果您需要自定义 TLS 对等名称,也可以使用它。
HappyEyeBallsConnector
类实现ConnectorInterface
并允许您创建到任何主机名-端口组合的纯文本 TCP/IP 连接。它在内部实现了RFC6555
和RFC8305
中的 happy eyeballs 算法,以支持 IPv6 和 IPv4 主机名。
它通过装饰给定的TcpConnector
实例来实现此目的,以便它首先通过 DNS(如果适用)查找给定的域名,然后建立到解析的目标 IP 地址的底层 TCP/IP 连接。
确保设置您的 DNS 解析器和底层 TCP 连接器,如下所示:
$ dnsResolverFactory = new React Dns Resolver Factory ();
$ dns = $ dnsResolverFactory -> createCached ( ' 8.8.8.8 ' );
$ dnsConnector = new React Socket HappyEyeBallsConnector ( null , $ tcpConnector , $ dns );
$ dnsConnector -> connect ( ' www.google.com:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
另请参阅示例。
可以通过取消其挂起的承诺来取消挂起的连接尝试,如下所示:
$ promise = $ dnsConnector -> connect ( ' www.google.com:80 ' );
$ promise -> cancel ();
对待处理的 Promise 调用cancel()
将取消底层 DNS 查找和/或底层 TCP/IP 连接并拒绝生成的 Promise。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递要用于此对象的事件循环实例。您可以在此处使用null
值以使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应给出此值。
高级用法:在内部,
HappyEyeBallsConnector
依赖Resolver
来查找给定主机名的 IP 地址。然后,它将用此 IP 替换目标 URI 中的主机名,并附加hostname
查询参数,并将此更新的 URI 传递到底层连接器。 Happy Eye Balls 算法描述查找给定主机名的 IPv6 和 IPv4 地址,以便此连接器发出 A 和 AAAA 记录的两个 DNS 查找。然后,它使用所有 IP 地址(v6 和 v4)并尝试以 50 毫秒的间隔连接到所有这些地址。在 IPv6 和 IPv4 地址之间切换。建立连接后,所有其他 DNS 查找和连接尝试都将被取消。
DnsConnector
类实现ConnectorInterface
,并允许您创建到任何主机名-端口组合的纯文本 TCP/IP 连接。
它通过装饰给定的TcpConnector
实例来实现此目的,以便它首先通过 DNS(如果适用)查找给定的域名,然后建立到解析的目标 IP 地址的底层 TCP/IP 连接。
确保设置您的 DNS 解析器和底层 TCP 连接器,如下所示:
$ dnsResolverFactory = new React Dns Resolver Factory ();
$ dns = $ dnsResolverFactory -> createCached ( ' 8.8.8.8 ' );
$ dnsConnector = new React Socket DnsConnector ( $ tcpConnector , $ dns );
$ dnsConnector -> connect ( ' www.google.com:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( ' ... ' );
$ connection -> end ();
});
另请参阅示例。
可以通过取消其挂起的承诺来取消挂起的连接尝试,如下所示:
$ promise = $ dnsConnector -> connect ( ' www.google.com:80 ' );
$ promise -> cancel ();
对待处理的 Promise 调用cancel()
将取消底层 DNS 查找和/或底层 TCP/IP 连接并拒绝生成的 Promise。
高级用法:在内部,
DnsConnector
依赖ReactDnsResolverResolverInterface
来查找给定主机名的 IP 地址。然后,它将用此 IP 替换目标 URI 中的主机名,并附加hostname
查询参数,并将此更新的 URI 传递到底层连接器。因此,底层连接器负责创建到目标 IP 地址的连接,而此查询参数可用于检查原始主机名,并由TcpConnector
用于设置 TLS 对等名称。如果显式给出hostname
,则不会修改此查询参数,如果您想要自定义 TLS 对等名称,这会很有用。
SecureConnector
类实现ConnectorInterface
,并允许您创建与任何主机名-端口组合的安全 TLS(以前称为 SSL)连接。
它通过装饰给定的DnsConnector
实例来实现此目的,以便它首先创建纯文本 TCP/IP 连接,然后在此流上启用 TLS 加密。
$ secureConnector = new React Socket SecureConnector ( $ dnsConnector );
$ secureConnector -> connect ( ' www.google.com:443 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( " GET / HTTP/1.0 rn Host: www.google.com rnrn" );
. . .
});
另请参阅示例。
可以通过取消其挂起的承诺来取消挂起的连接尝试,如下所示:
$ promise = $ secureConnector -> connect ( ' www.google.com:443 ' );
$ promise -> cancel ();
在未决承诺上呼叫cancel()
将取消基础的TCP/IP连接和/或SSL/TLS谈判,并拒绝由此产生的承诺。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递事件循环实例以用于此对象。您可以在此处使用null
值,以便使用默认循环。除非您确定要明确使用给定的事件循环实例,否则不应给出此值。
您可以选择将其他SSL上下文选项传递给构造函数:
$ secureConnector = new React Socket SecureConnector ( $ dnsConnector , null , [
' verify_peer ' => false ,
' verify_peer_name ' => false
]);
默认情况下,该连接器支持TLSV1.0+,并不包括对Legacy SSLV2/SSLV3的支持。您还可以明确选择要与远程协商的TLS版本:
$ secureConnector = new React Socket SecureConnector ( $ dnsConnector , null , [
' crypto_method ' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
]);
高级用法:在内部,
SecureConnector
依靠在基础流资源上设置所需的上下文选项。因此,它应与连接器堆栈中某个地方的TcpConnector
一起使用,以便为每个流资源分配一个空的上下文资源并验证PEER名称。不这样做可能会导致TLS对等名称不匹配错误或一些难以追踪种族条件,因为所有流资源都将使用单个,共享的默认上下文资源。
TimeoutConnector
类实现了ConnectorInterface
,并允许您将超时处理添加到任何现有的连接器实例。
它通过装饰任何给定的ConnectorInterface
实例并启动一个计时器来做到这一点,该计时器将自动拒绝并流产任何基础连接尝试,如果它花费的时间太长。
$ timeoutConnector = new React Socket TimeoutConnector ( $ connector , 3.0 );
$ timeoutConnector -> connect ( ' google.com:80 ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
// connection succeeded within 3.0 seconds
});
另请参阅任何示例。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递事件循环实例以用于此对象。您可以在此处使用null
值,以便使用默认循环。除非您确定要明确使用给定的事件循环实例,否则不应给出此值。
可以通过取消其未决承诺来取消待处理的连接尝试:
$ promise = $ timeoutConnector -> connect ( ' google.com:80 ' );
$ promise -> cancel ();
在未决承诺上呼叫cancel()
将取消基础连接的尝试,中止计时器并拒绝由此产生的承诺。
UnixConnector
类实现了ConnectorInterface
,并允许您连接到类似的Unix域套接字(UDS)路径:
$ connector = new React Socket UnixConnector ();
$ connector -> connect ( ' /tmp/demo.sock ' )-> then ( function ( React Socket ConnectionInterface $ connection ) {
$ connection -> write ( " HELLO n" );
});
连接到UNIX域插座是一个原子操作,即立即定居(解决或拒绝)。因此,在由此产生的承诺上调用cancel()
无效。
getRemoteAddress()
方法将返回给connect()
方法给出的目标unix域套接字(uds)路径,用unix://
scheme parded,例如unix:///tmp/demo.sock
。getLocalAddress()
方法很可能会返回null
值,因为此值不适用于此处的UDS连接。
此类采用可选的LoopInterface|null $loop
参数,该参数可用于传递事件循环实例以用于此对象。您可以在此处使用null
值,以便使用默认循环。除非您确定要明确使用给定的事件循环实例,否则不应给出此值。
FixedUriConnector
类别实现ConnectorInterface
并装饰现有的连接器,以始终使用固定的预配置URI。
这对于不支持某些URI的消费者很有用,例如您要明确连接到Unix域套接字(UDS)路径,而不是连接到高级API假定的默认地址:
$ connector = new React Socket FixedUriConnector (
' unix:///var/run/docker.sock ' ,
new React Socket UnixConnector ()
);
// destination will be ignored, actually connects to Unix domain socket
$ promise = $ connector -> connect ( ' localhost:80 ' );
推荐的安装此库的方法是通过 Composer。作曲家新手?
一旦发布,该项目将遵循 SemVer。目前,这将安装最新的开发版本:
composer require react/socket:^3@dev
有关版本升级的详细信息,另请参阅变更日志。
该项目旨在在任何平台上运行,因此不需要任何 PHP 扩展,并支持在 PHP 7.1 到当前 PHP 8+ 上运行。强烈建议为此项目使用最新支持的 PHP 版本。
传统php <7.3.3(和php <7.2.15)遭受一个错误,其中Feof()可能会在零碎的TLS记录上使用100%CPU使用。我们尝试始终立即消耗完整的接收缓冲区,以避免在TLS缓冲区中陈旧数据,以解决此问题。众所周知,这可以涉及高度cpu的高度使用,但这可能会导致很大的数据块对于高吞吐量方案。由于网络I/O缓冲区或受影响版本的恶意同行,强烈建议升级,因此仍然可以触发越野车行为。
旧版PHP <7.1.4一次通过TLS流编写大量数据时会受到错误。我们试图通过将写入块大小限制为仅适用于较旧的PHP版本的8192字节来解决这个问题。这只是一个工作,对受影响的版本有明显的性能罚款。
要运行测试套件,您首先需要克隆此存储库,然后通过 Composer 安装所有依赖项:
composer install
要运行测试套件,请转到项目根目录并运行:
vendor/bin/phpunit
该测试套件还包含许多依赖稳定Internet连接的功能集成测试。如果您不想运行这些,它们可以像这样跳过:
vendor/bin/phpunit --exclude-group internet
麻省理工学院,请参阅许可证文件。