如何加密非阻塞 PHP 套接字流? [英] How to encrypt non-blocking PHP socket streams?

查看:32
本文介绍了如何加密非阻塞 PHP 套接字流?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图以非阻塞(异步)方式使用 PHP 的 stream_socket_client() 函数.PHP 网站上的文档表明 STREAM_CLIENT_ASYNC_CONNECT 选项标志应启用此功能.但是,下面的代码...

I am attempting to use PHP's stream_socket_client() function in a non-blocking (asynchronous) fashion. Documentation on PHP's web site indicates that the STREAM_CLIENT_ASYNC_CONNECT option flag should enable this. However, the following code...

$start_time = microtime(true);
$sockets[$i] = stream_socket_client('ssl://74.125.47.109:993', $errint, $errstr, 1, STREAM_CLIENT_ASYNC_CONNECT);
$end_time = microtime(true);
echo "Total time taken: " . ($end_time-$start_time) . " secs.";

输出以下内容:

Total time taken: 0.76204109191895 secs.

显然,该函数是阻塞的(也可以通过省略 STREAM_CLIENT_ASYC_CONNECT 标志并没有有意义地改变所用的总时间"脚本输出这一事实来支持.

Evidently, the function is blocking (also supported by the fact that omission of the STREAM_CLIENT_ASYC_CONNECT flag does not meaningfully change the "total time taken" script output.

关于为什么会发生这种情况以及如何强制执行非阻塞连接尝试的任何想法?

Any ideas on why this may be happening, and how to enforce a non-blocking connection attempt?

推荐答案

为什么 ssl://包装器方法不起作用...

此时无法在 PHP 中使用 ssl:// 系列流包装器来建立非阻塞连接,原因很简单:

Why the ssl:// wrapper approach doesn't work ...

It's impossible to use the ssl:// family of stream wrappers to establish non-blocking connections in PHP at this time and the reason is simple:

要协商 SSL/TLS 握手,您必须同时发送和接收数据.

在不阻塞脚本执行的情况下,您根本无法在单个操作(例如流包装器的作用)中复制这样的信息.并且因为 PHP 最初设计为在严格同步的环境中运行(即每个请求都有自己的进程的阻塞 Web SAPI),所以这种阻塞行为是很自然的事情.

You simply can't duplex information like this inside a single operation (e.g. what the stream wrappers do) without blocking script execution. And because PHP was originally designed to function in strictly synchronous environments (i.e. the blocking web SAPIs where each request has its own process) this blocking behavior is the natural thing to do.

因此,即使您设置了 STREAM_CLIENT_ASYNC_CONNECT 标志,ssl:// 流包装器也不会按照您希望的方式运行.但是,仍然可以在非阻塞套接字操作中使用 PHP 的流加密功能.

As a result, the ssl:// stream wrapper won't operate how you want it to even if you set the STREAM_CLIENT_ASYNC_CONNECT flag. However, it is still possible to use PHP's stream encryption capabilities in your non-blocking socket operations.

SSL/TLS 协议在底层数据传输协议之上执行.这意味着我们只在 TCP/UDP/etc 之后启用加密协议.连接建立.因此,我们能够首先使用 STREAM_CLIENT_ASYC_CONNECT 异步标志连接到远程方,然后使用 stream_socket_enable_crypto().

The SSL/TLS protocols are executed on top of the underlying data transport protocol. This means that we only enable the encryption protocols after the TCP/UDP/etc. connection is established. As a result we're able to first connect to the remote party using the STREAM_CLIENT_ASYC_CONNECT async flag and subsequently enable crypto on the (now connected) socket using stream_socket_enable_crypto().

此示例假设您了解如何使用 stream_select()(或等效的描述符通知库以非阻塞方式处理套接字).没有处理潜在的套接字错误.

This example assumes you understand how to use stream_select() (or equivalent descriptor notification lib to work with sockets in a non-blocking way). There is no handling for potential socket errors.

<?php // connect + encrypt a socket asynchronously

$uri = 'tcp://www.google.com:443';
$timeout = 42;
$flags = STREAM_CLIENT_ASYNC_CONNECT;
$socket = stream_socket_client($uri, $errno, $errstr, $timeout, $flags);
stream_set_blocking($socket, false);

// Once the async connection is actually established it will be "writable."
// Here we use stream_select to find out when the socket becomes writable.
while (1) {
    $w = [$socket];
    $r = $e = [];
    if (stream_select($r, $w, $e, 30, 0)) {
        break; // the async connect is finished
    }
}

// Now that our socket is connected lets enable crypto
$crypto = STREAM_CRYPTO_METHOD_TLS_CLIENT;
while (1) {
    $w = [$socket];
    $r = $e = [];
    if (stream_select($r, $w, $e, 30, 0)) {
        break; // the async connect is finished
        $result = stream_socket_enable_crypto($socket, $enable=true, $crypto);
        if ($result === true) {
            // Crypto enabled, we're finished!
            break;
        } elseif ($result === false) {
            die("crypto failed :(");
        } else {
            // handshake isn't finished yet. Do another trip around the loop.
        }
    }
}

// Send/receive encrypted data on $socket here

关于返回值的说明

在检查加密启用调用的结果时,使用 === 等式非常重要.如相关手册条目所述:

Note on return values

It's very important to use the === equality when checking the results from our crypto enabling calls. As mentioned in the relevant manual entry:

成功时返回 TRUE,如果协商失败则返回 FALSE,如果没有足够的数据则返回 0,您应该重试(仅适用于非阻塞套接字).

Returns TRUE on success, FALSE if negotiation has failed or 0 if there isn't enough data and you should try again (only for non-blocking sockets).

如果我们不使用===,我们就无法区分false0.

If we don't use === we can't differentiate between false and 0.

这篇关于如何加密非阻塞 PHP 套接字流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆