【PHP8.x】socket_set_timeout関数の使い方

socket_set_timeout関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

socket_set_timeout関数は、ソケットに対するタイムアウトを設定する関数です。具体的には、ソケットに対する読み込みまたは書き込み操作が、指定された秒数およびマイクロ秒数を超えてブロックされるのを防ぎます。タイムアウトを設定することで、ネットワークの遅延や相手方の応答がない場合に、プログラムが無限に待ち続けることを回避できます。

この関数は、ソケットリソースと、タイムアウト時間を秒単位で指定するsecondsパラメータ、マイクロ秒単位で指定するmicrosecondsパラメータを受け取ります。secondsmicrosecondsを適切に設定することで、ソケット操作がタイムアウトするまでの正確な時間を制御できます。

タイムアウトが発生した場合、ソケットに対する次の操作は失敗し、エラーが発生します。これにより、エラー処理を行い、必要に応じて接続を再試行したり、ユーザーにエラーを通知したりするなど、適切な対応を取ることが可能になります。タイムアウトを設定しない場合、ソケット操作はデフォルトのタイムアウト設定(通常はOSによって設定される)に従うか、あるいは無期限にブロックされる可能性があります。そのため、ネットワークプログラミングにおいては、socket_set_timeout関数を使用して適切なタイムアウトを設定することが、プログラムの安定性と応答性を維持するために重要です。タイムアウトは、ソケット接続の確立時だけでなく、データの送受信時にも有効です。

構文(syntax)

1socket_set_timeout(Socket $socket, int $seconds, int $microseconds = 0): bool

引数(parameters)

Socket $socket, int $seconds, int $microseconds = 0

  • Socket $socket: タイムアウトを設定するソケットリソース
  • int $seconds: 秒単位のタイムアウト値
  • int $microseconds: マイクロ秒単位のタイムアウト値(デフォルトは0)

戻り値(return)

bool

socket_set_timeout関数は、指定したソケットリソースのタイムアウト設定が成功したかどうかに応じて、真偽値(trueまたはfalse)を返します。

サンプルコード

PHP: socket_set_timeout で読み書きタイムアウトを設定する

1<?php
2
3// ターゲットホストとポート
4// Google Public DNSのIPアドレス (TCPポート 53 はDNS over TCPに使用される)
5// この例では、接続は成功するものの、通常のHTTPリクエストなどには応答しないため、
6// socket_read がタイムアウトする挙動を確認するのに適しています。
7$host = '8.8.8.8';
8$port = 53;
9
10// ソケットの読み書き操作に適用されるタイムアウト秒数
11$seconds = 3;       // タイムアウト秒数
12$microseconds = 0;  // タイムアウトマイクロ秒数
13
14// 1. ソケットの作成
15// AF_INET: IPv4ドメインを使用
16// SOCK_STREAM: TCPプロトコル (信頼性のある接続指向ストリーム)
17// SOL_TCP: TCPプロトコルを指定 (通常は0で自動判別も可能)
18$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
19if ($socket === false) {
20    // ソケット作成失敗時のエラーメッセージを表示し、スクリプトを終了
21    echo "エラー: ソケットの作成に失敗しました - " . socket_strerror(socket_last_error()) . PHP_EOL;
22    exit(1);
23}
24echo "ソケットを正常に作成しました。" . PHP_EOL;
25
26// 2. ソケットの接続
27// socket_connect() 関数自体にはタイムアウトの引数がありません。
28// この関数はOSのTCP/IPスタックのタイムアウト設定に依存します。
29// 接続自体に厳密なタイムアウトを設ける場合は、ソケットをノンブロッキングモードにして
30// socket_select() を使用するか、stream_socket_client() の connect_timeout オプションを使用するのが一般的です。
31echo "{$host}:{$port} への接続を試みています..." . PHP_EOL;
32$result = @socket_connect($socket, $host, $port); // @でエラー出力を一時的に抑制
33
34if ($result === false) {
35    // 接続失敗時のエラーメッセージを表示し、ソケットを閉じ、スクリプトを終了
36    echo "エラー: 接続に失敗しました - " . socket_strerror(socket_last_error($socket)) . PHP_EOL;
37    socket_close($socket);
38    exit(1);
39}
40echo "{$host}:{$port} に正常に接続しました。" . PHP_EOL;
41
42// 3. 接続したソケットにタイムアウトを設定
43// socket_set_timeout() 関数は、接続後の socket_read(), socket_write() などの
44// データ送受信操作に適用されるタイムアウトを設定します。
45echo "ソケットの読み書きタイムアウトを {$seconds}{$microseconds} マイクロ秒に設定します..." . PHP_EOL;
46$timeoutSet = socket_set_timeout($socket, $seconds, $microseconds);
47
48if ($timeoutSet === false) {
49    // タイムアウト設定失敗時のエラーメッセージを表示し、ソケットを閉じ、スクリプトを終了
50    echo "エラー: タイムアウトの設定に失敗しました - " . socket_strerror(socket_last_error($socket)) . PHP_EOL;
51    socket_close($socket);
52    exit(1);
53}
54echo "ソケットの読み書きタイムアウトを正常に設定しました。" . PHP_EOL;
55
56// 4. 設定したタイムアウトをテストするための読み込み操作
57// ダミーのリクエストを送信します。
58// DNSポートなので、このリクエストに対する有効な応答は期待できません。
59$dummyRequest = "Hello\r\n";
60socket_write($socket, $dummyRequest, strlen($dummyRequest));
61echo "ダミーデータを送信しました。サーバーからの応答を {$seconds} 秒待機します..." . PHP_EOL;
62
63$buffer = '';
64$response = '';
65// socket_read は設定されたタイムアウトに従ってブロックします。
66// 有効な応答がない場合、設定した秒数でタイムアウトするはずです。
67while (($buffer = socket_read($socket, 2048, PHP_BINARY_READ)) !== false) {
68    if ($buffer === '') {
69        // データが読み込めなかった場合 (EOF またはタイムアウト)
70        $status = socket_get_status($socket);
71        if ($status['timed_out']) {
72            echo "情報: socket_read() がタイムアウトしました。" . PHP_EOL;
73        } elseif ($status['eof']) {
74            echo "情報: サーバーが接続を閉じました (EOF)。" . PHP_EOL;
75        } else {
76            echo "情報: socket_read() が空のデータを返しました (その他の理由)。" . PHP_EOL;
77        }
78        break; // ループを終了
79    }
80    $response .= $buffer;
81}
82
83if ($response === '') {
84    echo "情報: サーバーからの応答はありませんでした。" . PHP_EOL;
85} else {
86    echo "受信した応答:\n" . $response . PHP_EOL;
87}
88
89// 5. ソケットを閉じる
90socket_close($socket);
91echo "ソケットを閉じました。" . PHP_EOL;
92

このサンプルコードは、PHPのsocket_set_timeout関数を使い、ネットワーク通信におけるデータ読み書きのタイムアウト時間を設定する方法を示します。まず、socket_createでソケットを作成し、socket_connectで指定したサーバーへ接続します。次に、中心となるsocket_set_timeout関数を呼び出します。この関数は、第1引数に対象のソケット、第2引数に秒、第3引数にマイクロ秒でタイムアウト値を指定し、設定が成功すればtrue、失敗すればfalseを返します。重要なのは、この設定が接続後のsocket_readsocket_writeといったデータ送受信操作に適用されるという点です。接続処理そのもののタイムアウトは制御しません。設定後、socket_readでデータの受信を試みますが、この例では応答が返ってこないため、設定した時間が経過するとタイムアウトが発生します。このように、socket_set_timeoutは、応答のない相手との通信でプログラムが無限に待ち続けるのを防ぐために不可欠です。最後にsocket_closeでソケットを閉じて通信を終了します。

socket_set_timeout関数は、ソケット接続が完了した後のデータ読み書き操作にのみタイムアウトを設定します。最も重要な注意点は、この設定がsocket_connect関数による接続処理自体には影響しないことです。接続に厳密なタイムアウトを設けたい場合は、ノンブロッキングソケットとsocket_select関数を組み合わせるなど、別の手法が必要になります。また、データ読み込み時にタイムアウトが発生するとsocket_read関数はfalseを返しますが、これがタイムアウトが原因かを確認するためには、サンプルコードのようにsocket_get_status関数でソケットの状態を必ずチェックするようにしてください。

関連コンテンツ

【PHP8.x】socket_set_timeout関数の使い方 | いっしー@Webエンジニア