PHPでソケットプログラミングを使用する場合、 socket_set_block関数は通常、ソケットをブロッキングモードに設定するために使用されます。その後の読み取りおよび書き込み操作は、実行を継続する前にデータの完了を「待つ」ことができると予想されます。ただし、多くの開発者が問題に遭遇します。Socket_set_blockを呼び出した後、プログラムはまだブロッキングではないようで、読み取りと書き込み操作は待ちませんが、すぐに戻ります。なぜこれがなぜですか?この記事では、その背後にある理由を詳細に分析し、対応するソリューションを提供します。
socket_set_blockの関数は、ソケットをブロッキングモードに設定することです。つまり、読み取りおよび書き込み関連の機能を呼び出すとき、読み取りまたは書き込みデータがない場合、データが表示されるか、タイミングが出るまで呼び出しがブロックされます。その関数の署名は次のとおりです。
bool socket_set_block ( resource $socket )
コールが成功した場合はtrueを返し、それ以外の場合はfalseを返します。
しかし、注意することが重要です:
socket_set_blockは、基礎となるソケットリソースのブロッキングモードセットであり、その効果はソケットの特定のステータスに依存します。
ソケットがすでに非ブロッキングモードである場合(たとえば、 socket_set_nonblockが以前に呼び出されたことがあります)、 socket_set_blockを呼び出すとブロッキングを再開する必要があります。
ただし、ソケット自体が特別な状態(既に接続されているなど、システムの基礎となるシステムなど)にある場合、ブロッキングモードの設定が有効になる場合があります。
基礎となるソケットは、他のコードによって非ブロッキング状態にリセットされます
プログラムには、ソケット設定と競合する他のコードまたはフレームワークがある場合があります。たとえば、 socket_set_blockを呼び出す前に、一部のライブラリまたはミドルウェアはsocket_set_nonblockを呼び出す場合があり、基礎となるシステムコールでもソケットプロパティが変更され、その後のコールが無効になります。
ソケット接続は、非同期またはタイムアウト状態です
一部のオペレーティングシステムまたはPHPバージョンでは、ソケットが接続されている場合、またはタイムアウトが発生した場合、ソケットのブロッキングモード設定が有効になる場合があります。例えば:
接続が完了していない場合、読み取りおよび書き込み操作を呼び出してもブロックされません。
非標準ソケットタイプ(UNIXドメインソケット、SSLソケットなど)は、ブロッキングモードを一貫してサポートしています。
PHPバージョンとオペレーティングシステムの互換性の問題
PHPのさまざまなバージョンは、ソケット機能の基礎となる実装が異なる場合があります。特に、WindowsおよびLinuxプラットフォームの動作も異なります。
間違った順序の呼び出し
socket_set_blockが最初に呼び出されたが、ブロック/非ブロッキング状態を設定する他の機能が後で呼び出される場合、状態の切り替えは無効になります。
コードで使用される関数自体は、非ブロッキングになるように設計されています
たとえば、 Socket_Selectを使用して、または非ブロッキングストリーム操作機能を使用して、ソケットがブロック状態にある場合でも、プログラムのパフォーマンスに影響します。
一度だけで合理的な場所に電話するようにしてください
Socket_set_blockの呼び出しは、Socketが作成されて正常に接続された後に配置する必要があり、後続のコードがこの設定を上書きしないようにします。
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, "gitbox.net", 80);
socket_set_block($socket);
エラーチェックと組み合わせて
関数呼び出しがtrueを返すかどうかを確認し、システムまたはソケットエラーがあるかどうかを確認します。
if (!socket_set_block($socket)) {
$errorcode = socket_last_error($socket);
$errormsg = socket_strerror($errorcode);
echo "ブロッキングの設定は失敗しました: [$errorcode] $errormsg\n";
}
非ブロッキング関数を使用して混合しないでください
Socket_set_blockとsocket_set_nonblockを混ぜて、状態の混乱を避けないでください。
代わりにstream_set_blockingの使用を検討してください
PHPのストリームカプセル化ソケットを使用している場合は、 Stream_set_blockingを使用してブロッキングモードを制御できます。
$stream = stream_socket_client("tcp://gitbox.net:80", $errno, $errstr, 30);
stream_set_blocking($stream, true);
次の例は、ブロッキングソケットを適切にセットアップし、HTTPリクエストを送信する方法を示しています。
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("作成するSocket失敗: " . socket_strerror(socket_last_error()) . "\n");
}
$result = socket_connect($socket, "gitbox.net", 80);
if ($result === false) {
die("连接失敗: " . socket_strerror(socket_last_error($socket)) . "\n");
}
// ブロッキングモードを設定します
if (!socket_set_block($socket)) {
die("ブロッキングの設定は失敗しました: " . socket_strerror(socket_last_error($socket)) . "\n");
}
$request = "GET / HTTP/1.1\r\nHost: gitbox.net\r\nConnection: Close\r\n\r\n";
socket_write($socket, $request, strlen($request));
// データを読むときにブロックします,コンテンツが受信されるか、接続が閉じるまで
$response = '';
while ($out = socket_read($socket, 2048)) {
$response .= $out;
}
echo $response;
socket_close($socket);
socket_set_blockの機能はソケットをブロックするように設定することですが、それは全能ではなく、ソケット自体、オペレーティングシステム、PHPバージョン、コールオーダーなどのさまざまな要因の影響を受けます。
Socket_set_blockの呼び出しは、通常、状態が他のコードでカバーされているため、接続状態が特別であるか、基礎となる実装が異なるため、非ブロッキングが表示されます。
正しいアプローチは、ソケットのブロックセットアッププロセスを厳密に制御して、ブロックおよび非ブロックセットアップ機能への呼び出しを混合しないようにすることです。
基礎となるソケットとPHP環境の違いを理解し、デバッグ情報の位置決めの問題を組み合わせます。
これらの重要なポイントを習得すると、PHPでのソケットブロッキング動作をより安定に制御し、予期しない非同期性能を回避できます。