ネットワークプログラミングにPHPを使用する場合、 Socket_Accept()を使用してTCPサーバーを作成する場合、多くの開発者はしばしば問題に遭遇します。プログラムは立ち往生しており、実行し続けることができません。この現象は、主に「ブロッキングモード」と「非ブロッキングモード」を十分に理解していないことに起因しています。この記事では、Socket_accept()が「スタック」する理由を説明し、ブロッキングモードと非ブロッキングモードの違いを詳細に説明します。
socket_accept()は、クライアントからの接続要求を受け入れるPHPソケット拡張機能の関数です。 socket_create()を使用してソケットを作成し、 socket_bind()とsocket_listen()を使用してリスニングを開始したら、 socket_accept()を使用して接続を受信する必要があります。
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 8080);
socket_listen($socket);
while (true) {
$client = socket_accept($socket);
// 右 $client 読み取りおよび書き込み操作を実行します
}
上記の例では、 socket_accept()は、クライアントが接続するまで接続を待っている間、ブロックを続けます。
いわゆる「スタック」は、実際にプログラムが接続を待っているが、戻っていないことです。これはバグではなく、ブロッキングモードの通常の動作です。
デフォルトでは、PHPソケットはブロッキングモードです。これはつまり:
クライアントがサーバーに接続されていない場合、 socket_accept()が待機します。
CPUは、socket_accept()が結果を返すまで後続のコードを実行しません。
そのため、クライアントを接続せずにデバッグまたはサーバーを実行している場合、プログラムは常にsocket_accept()行で「スタック」されます。
サーバーは接続を待つだけであるため、これは生産環境では受け入れられます。しかし、デバッグや複数のタスクを処理する必要がある環境では、このブロッキング動作が問題になる可能性があります。
特定の操作を定期的に実行したり、他のイベントを検出したりするなど、接続なしでプログラムを実行し続ける場合は、非ブロッキングモードを使用する必要があります。
socket_set_nonblock($socket);
この時点で、 socket_accept()はもはや待つことはありませんが、代わりに:
接続があるときにクライアントソケットに戻ります。
接続がないときにすぐにfalseを返し、エラーコードをsocket_eagainまたはsocket_ewouldblockに設定します。
サンプルコード:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 8080);
socket_listen($socket);
socket_set_nonblock($socket);
while (true) {
$client = @socket_accept($socket);
if ($client === false) {
// 接続なし,他のことを続けてください
echo "接続を待っています...\n";
sleep(1);
} else {
// 接続を正常に受け取りました
socket_write($client, "Hello from m66.net\n");
socket_close($client);
}
}
別の一般的な慣行は、 socket_select()を使用して、盲目のループチェックを避けることができるソケットがあるかどうかを検出することです。
$read = [$socket];
$write = $except = null;
if (socket_select($read, $write, $except, 5) > 0) {
$client = socket_accept($socket);
// クライアント接続を処理します
}
タイムアウト時間を設定できるため、この方法は無期限にブロックされません。また、複数のソケットが同時に聴くシーンにも適しており、高性能ネットワークプログラミングの一般的な手法です。
socket_accept()はデフォルトでブロックされており、接続要求がない場合は待ちます。
ブロッキングの動作はバグではありませんが、接続を待っている間にプログラムが「停滞」します。
Socket_set_nonblock()を非ブロッキングモードに設定して、プログラムの停滞を回避できます。
より高度なソリューションは、 socket_select()を使用して複数のソケットの状態を管理することです。