为什么 ssl:// 包装器方法不起作用......
不可能使用ssl://此时在 PHP 中使用一系列流包装器来建立非阻塞连接,原因很简单:
要协商 SSL/TLS 握手,您必须发送和接收数据。
您根本无法在单个操作(例如流包装器执行的操作)中重复这样的信息而不阻止脚本执行。而且由于 PHP 最初设计为在严格同步的环境中运行(即每个请求都有自己的进程的阻塞 Web SAPI),因此这种阻塞行为是很自然的事情。
结果,ssl://即使您设置了 STREAM_CLIENT_ASYNC_CONNECT 标志,流包装器也不会按照您希望的方式运行。然而,它is仍然可以在非阻塞套接字操作中使用 PHP 的流加密功能。
如何在非阻塞套接字流上启用加密...
SSL/TLS 协议在底层数据传输协议之上执行。这意味着我们只启用加密协议afterTCP/UDP/等。连接已建立。因此,我们能够首先使用 STREAM_CLIENT_ASYC_CONNECT 异步标志连接到远程方,然后使用以下命令在(现已连接的)套接字上启用加密:stream_socket_enable_crypto().
没有错误处理的简单示例
此示例假设您了解如何使用stream_select()
(或等效的描述符通知库以非阻塞方式使用套接字)。没有对潜在套接字错误的处理。
<?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
返回值注意事项
使用非常重要===
检查我们的加密启用调用的结果时是否相等。正如相关手册条目中提到的:
如果成功,则返回 TRUE;如果协商失败,则返回 FALSE;如果没有足够的数据,则返回 0,您应该重试(仅适用于非阻塞套接字)。
如果我们不使用===
我们无法区分false
and 0
.