Я пытаюсь использовать функцию stream_socket_client () PHP в неблокирующем (асинхронном) режиме. Документация на веб-сайте 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 не приводит к существенному изменению вывода сценария «общее время».
Любые идеи о том, почему это может происходить, и как обеспечить соблюдение попытки неблокирующего соединения?
Невозможно использовать семейство оболочек ssl: // для создания неблокирующих соединений в PHP в это время, и причина проста:
Чтобы согласовать рукопожатие SSL / TLS, вы должны отправлять и получать данные.
Вы просто не можете дублировать информацию, подобную этой, в рамках одной операции (например, что делают обертки потока), не блокируя выполнение сценария. И поскольку PHP изначально был предназначен для работы в строго синхронных средах (т. Е. В блокирующих веб-SAPI, где каждый запрос имеет свой собственный процесс), это поведение блокировки является естественным делом.
В результате оболочка потока ssl: // не будет работать так, как вы хотите, даже если вы установите флаг STREAM_CLIENT_ASYNC_CONNECT. Тем не менее, все еще возможно использовать возможности шифрования потока PHP в ваших неблокирующих операциях сокета.
Протоколы SSL / TLS выполняются поверх базового протокола передачи данных. Это означает, что мы разрешаем только протоколы шифрования после TCP / UDP / etc. соединение установлено. В результате мы можем сначала подключиться к удаленной стороне, используя флаг async STREAM_CLIENT_ASYC_CONNECT, а затем включить криптографию в (теперь подключенном) сокете с помощью stream_socket_enable_crypto()
.
В этом примере предполагается, что вы можете использовать stream_select()
(или эквивалентную библиотеку уведомлений lib для работы с сокетами неблокирующим способом). Невозможно обработать потенциальные ошибки сокета.
<?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
и 0
.