現在の位置: ホーム> 最新記事一覧> Socket_set_block関数を使用した後、なぜPHPスクリプトがスタックするのですか? socket_set_blockによって引き起こされるブロッキングの問題を解決する方法は?

Socket_set_block関数を使用した後、なぜPHPスクリプトがスタックするのですか? socket_set_blockによって引き起こされるブロッキングの問題を解決する方法は?

gitbox 2025-05-26

PHPソケットをプログラミングするとき、 Socket_set_block()関数を使用して、ソケットをブロッキングモードに設定します。ブロッキングモードとは、read( socket_read() )、write( socket_write() )、receive connection( socket_accept() )などの操作は、データが読み取られるか、書かれた、または接続が着信するまで待機することを意味します。これが明確でない場合、いくつかのシナリオでsocket_set_block()を使用すると、PHPスクリプト全体が「停滞」します。つまり、プログラムは応答せずに長い間そこに行き詰まります。

1.なぜsocket_set_block()がphpスクリプトを停止させるのですか?

socket_set_block()を使用した後、PHPはブロッキングモードに入り、ソケット操作の結果を待ちます。対応するリソース(データや接続など)が長い間戻らない場合、PHPスクリプトは待機し、後続のコードを実行し続けません。

典型的な例は次のとおりです。

 <?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'gitbox.net', 80);
socket_set_block($socket); // ブロッキングモードに設定します

// データを読んでみてください
$response = socket_read($socket, 2048);
echo $response;

socket_close($socket);
?>

サーバーgitbox.netがすぐにデータを返さないか、まったく応答しない場合、 socket_read()は待機し、スクリプトは「スタック」されます。この種のラグは、タイムアウトメカニズムがトリガーされない限りエラーをスローしませんが、タイムアウト時間はデフォルトで設定されていないため、無限に待機する可能性があります。

2。socket_set_block ()によって引き起こされるブロッキングの問題を解決する方法は?

1.タイムアウトを設定します

socket_set_option()を使用して、長期ブロッキングを防ぐために読み取りと書き込みのタイムアウト時間を設定します。

 $timeout = ['sec' => 5, 'usec' => 0]; // ASを設定します52番
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout);

これにより、ブロッキング操作がタイムアウトし、一定期間待ってから戻ってきます。これは、スクリプトが長い間反応しないことを避けます。

2。非ブロッキングモード(推奨方法)を使用します

スクリプトをブロックしたくない場合は、代わりにsocket_set_nonblock()を使用して、すべてのソケット操作をすぐに戻すことができます。

 socket_set_nonblock($socket);

ループとUsleep()を使用すると、非ブロッキング待機をシミュレートできます。

 $buffer = '';
while (true) {
    $chunk = @socket_read($socket, 2048);
    if ($chunk === false) {
        usleep(100000); // 寝る100ms
        continue;
    } elseif ($chunk === '') {
        break; // 接続が閉じます
    } else {
        $buffer .= $chunk;
    }
}

3. select()を使用して読み取り可能な状態を待ちます

socket_select()は、複数のソケットが読み書きができているかどうかを確認でき、ブロック操作を効率的に管理する方法です。

 $read = [$socket];
$write = NULL;
$except = NULL;
$tv_sec = 5; // 最も待っています52番

if (socket_select($read, $write, $except, $tv_sec) > 0) {
    $response = socket_read($socket, 2048);
    echo $response;
} else {
    echo "Socket タイムアウトまたは読み取り可能なデータ";
}

socket_select()は、ソケットに実際に読み取る前に読み取るデータがあるかどうかを判断でき、ブロッキング状態に直接入ることを避けます。

3。概要

socket_set_block()の使用は安全ではありませんが、コンテキストの非常に明示的な制御が必要です。ネットワークの遅延と反応しないサービスの問題により、永久に詰まったスクリプトを避けるために、非ブロッキングモードを使用するか、ブロッキングモードでタイムアウト時間を明示的に設定することをお勧めします。

リアルタイムの応答または同時処理が必要なシナリオの場合、 socket_set_nonblock()を使用するか、 socket_select()と組み合わせてプログラムの実行フローを柔軟に制御することをお勧めします。これにより、プログラムの堅牢性が向上するだけでなく、システムの応答効率も大幅に向上します。