Вопрос по ssl, php, sockets – Как зашифровать неблокирующие потоки сокетов PHP?

8

Я пытаюсь использовать функцию PHP stream_socket_client () неблокирующим (асинхронным) способом. Документация на веб-сайте PHP указывает, что флаг опции STREAM_CLIENT_ASYNC_CONNECT должен включать это. Тем не менее, следующий код ...

$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 не приводит к значительному изменению выходных данных сценария «общее время, затраченное»).

Любые идеи о том, почему это может происходить, и как обеспечить неблокирующую попытку подключения?

К вашему сведению - изменение флагов на STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT также не обеспечивает неблокирующее поведение. Peter Ritchie
Похоже, что поиск в Google указывает на некоторые проблемы с & quot; ssl: // & quot; и STREAM_CLIENT_ASYNC_CONNECT. Может быть, из-за этого отключены неблокирующие соединения? Или, может быть, это не фактическое соединение, которое не блокирует, а читает / пишет? Some programmer dude

Ваш Ответ

1   ответ
18
Why the ssl:// wrapper approach doesn't work ...

ssl:// Семейство потоковых упаковщиков для установки неблокирующих соединений в PHP на данный момент, и причина проста:

To negotiate an SSL/TLS handshake you must both send and receive data.

Вы просто не можете дуплексную информацию, подобную этой, внутри одной операции (например, что делают потоковые упаковщики), не блокируя выполнение сценария. И поскольку PHP изначально был разработан для работы в строго синхронных средах (то есть для блокирования веб-SAPI, где каждый запрос имеет свой собственный процесс), такое поведение блокировки является естественным.

В результатеssl:// Оболочка потока не будет работать так, как вы хотите, даже если вы установите флаг STREAM_CLIENT_ASYNC_CONNECT. Тем не менее, этоis все еще возможно использовать возможности потокового шифрования PHP в ваших неблокирующих операциях с сокетами.

How to enable encryption on your non-blocking socket streams ...

Протоколы SSL / TLS выполняются поверх основного протокола передачи данных. Это означает, что мы включаем только протоколы шифрованияafter TCP / UDP / и т. д. соединение установлено. В результате мы можем сначала подключиться к удаленной стороне с помощью асинхронного флага STREAM_CLIENT_ASYC_CONNECT и затем включить шифрование на (теперь подключенном) сокете, используяstream_socket_enable_crypto().

Simple example sans error handling

В этом примере предполагается, что вы понимаете, как использовать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
Note on return values

Очень важно использовать=== равенство при проверке результатов наших криптографических звонков. Как упомянуто в соответствующей ручной записи:

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).

Если мы не используем=== мы не можем различатьfalse а также0.

Отличный ответ! Спасибо за очень полезную информацию.
Чтобы включить шифрование сstream_socket_server а такжеstream_selectЯ должен был установить сокет на блокировку, прежде чемstream_socket_accept, После этого я мог немедленно вернуть его в неблокирующее состояние.

Похожие вопросы