PHP8/EventListener - 从文件描述符获取 Socket 实例
PHP8/EventListener - Getting Socket instance from file descriptor
PHP8 引入了 Socket class 来替换旧的套接字资源。现在所有套接字函数都适用于此 class.
的实例
EventListener 构造函数的第二个参数 (https://www.php.net/manual/en/class.eventlistener.php) is callback function. It is called when new connection event arises. There is signature of this callback function: https://www.php.net/manual/en/eventlistener.setcallback.php 。它接收文件描述符作为第二个参数。
文件描述符是数值,但我需要 Socket 的实例 class,因为接下来我会将它传递给 EventBufferEvent 的构造函数。
请指教如何为这个文件描述符获取套接字实例?
无效的解决方案:
我试图找到绑定到文件描述符的地址和端口,然后创建新的套接字并将其绑定到这个地址和端口。但这会创建具有新文件描述符的新套接字,与事件侦听器回调中接收到的连接无关。
在将基于事件的套接字服务器迁移到 php8 时,我无法找到任何有用的文档或代码示例来解决我的问题。
我们对 Socket class 了解不多,PHP 文档说它是“完全不透明的 class”。因此,我必须查看 sockets.so
实现 sockets.c 的 C 源代码。我明白了,Socket
对象是由 php_socket
结构描述的。
我查看了头文件 php_sockets.h as well. It contains the function signature PHP_SOCKETS_API int socket_import_file_descriptor(PHP_SOCKET socket, php_socket *retsock);
. It receives file descriptor of socket connection and results with pointer to structure that defines instance of PHP Socket class. Let's check how socket_import_file_descriptor() 函数是在 sockets.c 中实现的。它可以满足我们的需求,但不会导出到 PHP 范围,因此我们无法调用它。
我在导出函数中搜索 socket_import_file_descriptor()
的用法。它仅从 socket_import_stream()
调用。让我们看看 socket_import_stream(resource $stream): Socket|false 的 PHP 文档。它通过 Socket 实例接收资源和结果。这是我们需要的,但文件描述符不是资源。但是,有办法打开文件描述符并获取其资源。这是解决方案:
$filename = sprintf("php://fd/%d", $clientResourceFd);
$clientResource = fopen($filename, 'rb');
$clientSocket = socket_import_stream($clientResource);
好消息它在 linux 中有效。任何建议,如何让它在 windows 中工作?
简短的回答是:您可以将数字文件描述符作为 2nd 参数传递给 EventBufferEvent::__construct
。
https://www.php.net/manual/en/eventlistener.setcallback.php . It receives file descriptor as the 2nd argument. File descriptor is numeric value, but I need instance of Socket class, because next I will pass it to constructor of EventBufferEvent.
EventBufferEvent::__construct
的文档中存在错误:
socket
May be created as a stream(not necessarily by means of sockets extension)
这有点误导,因为第二个参数实际上与大多数接受“fd”或“流”的事件函数一样灵活。事件尝试从参数中获取数值文件描述符,如果它已经是数值,则使用它。
查看 "Echo server" example 如何为每个连接创建 EventBufferEvent
的实例:
class MyListenerConnection {
public function __construct($base, $fd) {
$this->base = $base;
$this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);
// ...
$this->conn[] = new MyListenerConnection($base, $fd);
Use 也可以使用未记录的 EventUtil::createSocket
函数:
$socket = EventUtil::createSocket($fd);
socket_set_nonblock($socket);
$socketFd = EventUtil::getSocketFd($socket);
如果您将 $fd
与其他 类(例如 EventBufferEvent
)一起使用,请确保将对套接字的引用保存在某处。否则,$socket
变量可能与底层文件描述符一起关闭。
PHP8 引入了 Socket class 来替换旧的套接字资源。现在所有套接字函数都适用于此 class.
的实例EventListener 构造函数的第二个参数 (https://www.php.net/manual/en/class.eventlistener.php) is callback function. It is called when new connection event arises. There is signature of this callback function: https://www.php.net/manual/en/eventlistener.setcallback.php 。它接收文件描述符作为第二个参数。 文件描述符是数值,但我需要 Socket 的实例 class,因为接下来我会将它传递给 EventBufferEvent 的构造函数。
请指教如何为这个文件描述符获取套接字实例?
无效的解决方案:
我试图找到绑定到文件描述符的地址和端口,然后创建新的套接字并将其绑定到这个地址和端口。但这会创建具有新文件描述符的新套接字,与事件侦听器回调中接收到的连接无关。
在将基于事件的套接字服务器迁移到 php8 时,我无法找到任何有用的文档或代码示例来解决我的问题。
我们对 Socket class 了解不多,PHP 文档说它是“完全不透明的 class”。因此,我必须查看 sockets.so
实现 sockets.c 的 C 源代码。我明白了,Socket
对象是由 php_socket
结构描述的。
我查看了头文件 php_sockets.h as well. It contains the function signature PHP_SOCKETS_API int socket_import_file_descriptor(PHP_SOCKET socket, php_socket *retsock);
. It receives file descriptor of socket connection and results with pointer to structure that defines instance of PHP Socket class. Let's check how socket_import_file_descriptor() 函数是在 sockets.c 中实现的。它可以满足我们的需求,但不会导出到 PHP 范围,因此我们无法调用它。
我在导出函数中搜索 socket_import_file_descriptor()
的用法。它仅从 socket_import_stream()
调用。让我们看看 socket_import_stream(resource $stream): Socket|false 的 PHP 文档。它通过 Socket 实例接收资源和结果。这是我们需要的,但文件描述符不是资源。但是,有办法打开文件描述符并获取其资源。这是解决方案:
$filename = sprintf("php://fd/%d", $clientResourceFd);
$clientResource = fopen($filename, 'rb');
$clientSocket = socket_import_stream($clientResource);
好消息它在 linux 中有效。任何建议,如何让它在 windows 中工作?
简短的回答是:您可以将数字文件描述符作为 2nd 参数传递给 EventBufferEvent::__construct
。
https://www.php.net/manual/en/eventlistener.setcallback.php . It receives file descriptor as the 2nd argument. File descriptor is numeric value, but I need instance of Socket class, because next I will pass it to constructor of EventBufferEvent.
EventBufferEvent::__construct
的文档中存在错误:
socket
May be created as a stream(not necessarily by means of sockets extension)
这有点误导,因为第二个参数实际上与大多数接受“fd”或“流”的事件函数一样灵活。事件尝试从参数中获取数值文件描述符,如果它已经是数值,则使用它。
查看 "Echo server" example 如何为每个连接创建 EventBufferEvent
的实例:
class MyListenerConnection {
public function __construct($base, $fd) {
$this->base = $base;
$this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);
// ...
$this->conn[] = new MyListenerConnection($base, $fd);
Use 也可以使用未记录的 EventUtil::createSocket
函数:
$socket = EventUtil::createSocket($fd);
socket_set_nonblock($socket);
$socketFd = EventUtil::getSocketFd($socket);
如果您将 $fd
与其他 类(例如 EventBufferEvent
)一起使用,请确保将对套接字的引用保存在某处。否则,$socket
变量可能与底层文件描述符一起关闭。