ソケットプログラミングにPHPを使用する場合、多くの開発者がSocket_Accept()と最初に接触したときに問題に遭遇します。これは、高い並行性または複数の接続が同時に処理されるいくつかのシナリオでボトルネックになる可能性があります。
それで、なぜこれが起こるのですか? Socket_Select()を使用して、この問題を優雅にブロックして解決することを避けるにはどうすればよいですか?
socket_accept()は、リスニングソケットからの接続要求を受け入れるために使用される関数です。プロトタイプは次のとおりです。
resource socket_accept(resource $socket);
この関数の本質は、クライアント接続がない場合、接続が発生するまでプログラムの実行をブロックします。これは典型的なブロックI/O動作です。
この動作は、シングルスレッドサーバーでは望ましくありません。接続を待っているためにサーバーがブロックすると、他のタスクは完全に失敗します。
この問題を解決するために、PHPはSocket_Select()関数を提供します。これは、複数のソケットで「聞く」ために、どのソケットが読み取り/書き込み操作の準備ができているかを判断し、ブロッキングと待機を回避できます。
int socket_select(
array &$read,
array &$write,
array &$except,
int $tv_sec,
int $tv_usec = 0
);
聞きたいソケットを$ read配列に配置し、 socket_select()を呼び出す必要があります。ソケットが読み取られた場合(つまり、クライアント接続要求があるとき)、 socket_select()が返されるため、 socket_accept()を再度呼び出すとブロックされません。
socket_select()を使用した完全なサーバー側の例を次に示します。
<?php
set_time_limit(0);
// 作成する socket
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 8080);
socket_listen($server);
// 非ブロッキングモードを設定します(オプション,しかし、お勧めします)
socket_set_nonblock($server);
$clients = [];
while (true) {
$read_sockets = [$server] + $clients;
$write = $except = [];
// すべてを聞いてください sockets,タイムアウトはです 1 2番
$changed = socket_select($read_sockets, $write, $except, 1);
if ($changed === false) {
echo "socket_select() エラーが発生しました\n";
break;
}
// 新しい接続要求があります
if (in_array($server, $read_sockets)) {
$client = socket_accept($server);
if ($client !== false) {
socket_getpeername($client, $ip, $port);
echo "からの新しい接続 {$ip}:{$port}\n";
$clients[] = $client;
}
// から read リストからリスナーを削除します socket
$key = array_search($server, $read_sockets);
unset($read_sockets[$key]);
}
// 接続されているデータを処理します
foreach ($read_sockets as $sock) {
$data = @socket_read($sock, 1024, PHP_NORMAL_READ);
if ($data === false || trim($data) === '') {
// クライアントの切断
$key = array_search($sock, $clients);
unset($clients[$key]);
socket_close($sock);
continue;
}
$data = trim($data);
echo "受信したデータ:{$data}\n";
$response = "あなたはそれを送った:{$data}\n";
socket_write($sock, $response);
}
}
socket_close($server);
socket_accept()はブロックしており、シンプルで同期したシングルコネクトシナリオに適しています。
同時性をサポートするサーバーを構築するには、 Socket_Select()を使用してソケットのステータスを聞く必要があります。
Socket_Select()を使用すると、複数のソケットでイベントを「待機」することができ、ブロッキング操作のためにプログラム全体がハングすることを回避できます。
柔軟性を向上させるために、 socket_set_nonblock()と組み合わせて使用することをお勧めします。
上記の方法により、複数のクライアント接続を同時に処理できるレスポンシブPHPソケットサーバーを実装し、より複雑なネットワークプログラミングの基礎を築くことができます。
実際の開発では、ドメイン名または住所は、たとえば、独自のサービスアドレスに置き換えられます。