socket_select

与えられたソケットの配列に対し、指定した有効時間で select() システムコールを実行する

説明

intfalse socket_select(
    arraynull &$read,
    arraynull &$write,
    arraynull &$except,
    intnull $seconds,
    int $microseconds = 0
)

socket_select はソケットの配列を受け取り、 それらの状態が変化するまで待ちます。BSD のソケットについての知識がある方なら、 これらのソケットの配列が、いわゆるファイル記述子セットであることがご理解いただけるでしょう。 3 つの独立した配列でソケットを監視します。

パラメータ

read

配列 read に挙げられたソケットでは、 文字が読み込み可能になっているかどうか(厳密に言うと、読み込みが ブロックされていないかどうか - 実際には、ソケット記述子はファイルの 終端でも有効です。そのような場合、socket_read は長さゼロの文字列を返します)を監視します。

write

配列 write に挙げられたソケットでは、 書き込みがブロックされていないかどうかを監視します。

except

配列 except に挙げられたソケットでは、 例外を監視します。

seconds

seconds および microseconds は、ともにタイムアウトを指定するパラメータです。 タイムアウトは、socket_select が結果を返すまでの経過時間の最大値です。 seconds はゼロにすることも可能で、そうすると socket_select は結果をすぐに返します。 これはポーリングをする際に有用です。secondsnull(タイムアウトしない)を指定すると、 socket_select は無期限にブロックします。

microseconds

警告

終了時に配列は書き換えられ、 どのソケットの状態が変わったのかがわかるようになります。

socket_select のすべての配列を設定する必要はありません。 使用しないものについては空の配列や null をかわりに指定しておくことが可能です。 また、これらの配列は参照渡し であり、 socket_select をコールした後でその中身が書き換えられていることに注意しましょう。

注意:

現状の Zend Engine の制限により、関数の参照渡しパラメータに null のような定数値を直接渡すことができません。一時的な変数を使用するか、 あるいは一番左に一時変数を使用する式を使用してください。

例1 socket_select での null の使用

<?php
$e = NULL;
socket_select($r, $w, $e, 0);
?>

戻り値

成功した場合は、socket_select は配列内で 変化のあったソケットの数を返します。もし何かがおこる前に タイムアウト時間が経過した場合は、ゼロを返すことになります。 エラー時には false が返されます。エラーコードは socket_last_error で取得可能です。

注意:

エラーかどうかを調べる際には、必ず === 演算子を 使用するようにしましょう。socket_select は 0 を返す場合もあり、このような場合に == を用いて比較すると、エラーと判定されてしまいます。

例2 socket_select の返す結果を知る

<?php
$e = NULL;
if (false === socket_select($r, $w, $e, 0)) {
    echo "socket_select() は失敗しました。原因: " .
        socket_strerror(socket_last_error()) . "\n";
}
?>

例3 socket_select の例

<?php
/* 読み込み用の配列を準備する */
$read   = array($socket1, $socket2);
$write  = NULL;
$except = NULL;
$num_changed_sockets = socket_select($read, $write, $except, 0);

if ($num_changed_sockets === false) {
    /* エラー処理 */
} else if ($num_changed_sockets > 0) {
    /* すくなくともひとつのソケットで、何らかの出来事が起こっています */
}
?>

注意

注意:

ソケットの実装によっては、取り扱いに注意すべきものがあることを知っておいてください。 基本的なルールは以下のとおりです。

  • 基本的に socket_select のタイムアウトは 指定しないように心がけましょう。もしデータがなかった場合に、 プログラム側でそれを判定できなくなってしまいます。タイムアウトに 依存しているコードは移植性が悪く、デバッグが困難です。
  • socket_select のコール後に値をチェックして 適切に処理するつもりがないソケットは、決して配列に追加してはいけません。 socket_select から値が返ってきたあとは、 配列内のすべてのソケットをチェックする必要があります。 すべての書き込み用ソケットは書き込める必要がありますし、 またすべての読み込み用ソケットは読み込める必要があります。
  • 配列で返されたソケットに対して読み込み/書き込みをする場合には、 指定したデータを必ずしもすべて読み込み/書き込みするとは限らないことを 知っておいてください。たった 1 バイトしか読み込み/書き込みが できなかった場合にも対処できるよう準備しておきましょう。
  • ほとんどのソケット実装で、except でキャッチできる 例外はただひとつ、すなわちソケットが受け取ったデータが帯域外で あったということだけです。

参考

  • socket_read
  • socket_write
  • socket_last_error
  • socket_strerror