8

PHPのstream_socket_client()関数を非ブロッキング(非同期)方式で使用しようとしています。PHPのWebサイトのドキュメントには、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フラグを省略しても、「合計所要時間」スクリプト出力が意味のある形で変更されないという事実によってもサポートされています。

これが発生する理由、および非ブロッキング接続の試行を強制する方法についてのアイデアはありますか?

4

1 に答える 1

21

ssl:// ラッパー アプローチが機能しない理由 ...

現時点では、ストリーム ラッパーのssl://ファミリを使用して PHP で非ブロック接続を確立することはできません。理由は簡単です。

SSL/TLS ハンドシェイクをネゴシエートするには、データを送受信する必要があります。

スクリプトの実行をブロックせずに、単一の操作 (たとえば、ストリーム ラッパーが行うこと) 内でこのような情報を二重化することはできません。また、PHP は本来、厳密な同期環境 (つまり、各リクエストが独自のプロセスを持つブロッキング Web SAPI) で機能するように設計されているため、このブロッキング動作は当然のことです。

その結果、STREAM_CLIENT_ASYNC_CONNECT フラグを設定しても、ssl://ストリーム ラッパーは意図したとおりに動作しません。ただし、ノンブロッキング ソケット操作で PHP のストリーム暗号化機能を使用することは可能です。

ノンブロッキング ソケット ストリームで暗号化を有効にする方法 ...

SSL/TLS プロトコルは、基礎となるデータ転送プロトコルの上で実行されます。これは、TCP/UDP/etc の後にのみ暗号化プロトコルを有効にすることを意味します。接続が確立されます。その結果、最初に 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 を返します (非ブロック ソケットの場合のみ)。

使用しないと、と を===区別できません。false0

于 2014-02-25T02:34:38.043 に答える