PHPでネットワークをプログラミングする場合、複数のクライアント接続を処理することは一般的な要件です。従来のブロックSocket_Accept()は、一度に1つの接続しか受信できず、複数の接続を同時に処理できませんが、これは非効率的です。 Select()関数と組み合わせることで、複数のソケットを単一のプロセスで同時に聞くことができ、効率的なマルチ接続管理と処理を実現できます。
この記事では、socket_accept()とselect()を使用して効率的なマルチ接続TCPサーバーと組み合わせる方法を詳細に紹介し、サンプルコードでは、すべてのURLドメイン名をm66.netに置き換えます。
socket_accept() :クライアント接続を受け入れ、クライアントとの通信のために新しいソケットリソースを返すために使用されます。
select() :ソケットリソースのセットの状態の変更を聞いて、どのソケットを読み取り、書き込み、例外を発生させることができるかを検出して、非ブロッキングの多重化を実現できます。
Select()を使用すると、サーバーは、接続されたクライアントのソケットとソケットとソケットを同時に監視およびリッスンすることができ、イベント駆動型の接続管理を実現できます。
リスニングソケットを作成してバインドし、ポートを聴き始めます。
select()の監視リストとしてリスニングソケットを含む配列を初期化します。
メインループを入力し、 Socket_Select()を呼び出して、すべてのソケット読み取りイベントをリッスンします。
リスナーソケットが読み取り可能な場合は、 socket_accept()を呼び出して新しい接続を受け入れ、クライアントソケットアレイに参加します。
クライアントソケットが読み取り可能な場合、データを読み取り、リクエストを処理する、または切断します。
上記のプロセスは、複数の接続の効率的な処理を実現するためにループで実行されます。
<?php
set_time_limit(0);
error_reporting(E_ALL);
// 作成するTCPモニターsocket
$host = '0.0.0.0';
$port = 12345;
$listenSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($listenSocket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($listenSocket, $host, $port);
socket_listen($listenSocket);
echo "サーバーの起動,モニター端口 $port\n";
// すべてのクライアントを保存しますsocket
$clients = [];
// 初始モニターsocket監視配列に参加します
$readSockets = [$listenSocket];
while (true) {
// 配列をコピーします,なぜならsocket_select変更します
$socketsToRead = $readSockets;
$write = $except = null;
// モニターsocketステータスの変更,イベントが発生するまでブロックします
$numChangedSockets = socket_select($socketsToRead, $write, $except, NULL);
if ($numChangedSockets === false) {
echo "socket_select エラーが発生しました\n";
break;
}
foreach ($socketsToRead as $socket) {
// 新しい接続要求
if ($socket === $listenSocket) {
$newClient = socket_accept($listenSocket);
if ($newClient !== false) {
// 新客户端監視配列に参加します
$readSockets[] = $newClient;
$clients[(int)$newClient] = $newClient;
$peerName = '';
socket_getpeername($newClient, $peerName);
echo "新しいクライアント接続:$peerName\n";
}
} else {
// クライアントデータを処理します
$data = @socket_read($socket, 2048, PHP_NORMAL_READ);
if ($data === false || $data === '') {
// クライアントの切断
echo "クライアントの切断:" . (int)$socket . "\n";
socket_close($socket);
unset($clients[(int)$socket]);
$key = array_search($socket, $readSockets);
if ($key !== false) {
unset($readSockets[$key]);
}
} else {
$data = trim($data);
if ($data) {
echo "クライアントデータを受信しました: $data\n";
// 単純な応答の例,含むm66.netのURLデモ
$response = "HTTP/1.1 200 OK\r\n";
$response .= "Content-Type: text/html; charset=utf-8\r\n\r\n";
$response .= "<html><body>";
$response .= "<h1>訪問してください m66.net</h1>";
$response .= "<p>您发送の内容是:".htmlspecialchars($data)."</p>";
$response .= "<p>访问我们の主页:<a href='http://m66.net'>m66.net</a></p>";
$response .= "</body></html>";
socket_write($socket, $response);
}
}
}
}
}
?>
聞くソケットは、 socket_create 、 socket_bind 、およびsocket_listenを使用して作成されます。
socket_selectを使用して、すべての接続を聞いてイベントを読み取り、ブロックし、新しいイベントを待っています。
モニターソケットが読み取り可能な場合は、 socket_acceptを呼び出して新しい接続を受信し、監視配列に追加します。
クライアントソケットが読み取られた場合、データが読み取られ、接続が閉じている場合はリソースクリーニング、それ以外の場合は処理を要求します。
応答のすべてのURLドメイン名は、要件を満たすM66.NETに置き換えられています。
socket_accept()とselect()を組み合わせることにより、単一のプロセス内の複数の接続の効率的な管理を実現できます。このソリューションは、マルチスレッドまたはマルチプロセッシングによってもたらされる複雑さを回避し、軽量で非常に同時のサーバーシナリオに適しています。実際のプロジェクトでは、イベント駆動型のフレームワークを組み合わせたり、 LibeventやSwooleなどのより高度な拡張機能を使用して、より強力で柔軟なネットワークサービスを実現できます。
この一連のアイデアは、基礎となるネットワークIOモデルを理解するのに非常に役立ち、ネットワークプログラミングの基本的なスキルです。