PHPのsocket_accept()関数を使用してソケットベースのサーバーアプリケーションを作成する場合、開発者はしばしば頭痛に遭遇します。これにより、通常、サーバーは新しいクライアント接続を受け入れなくなり、サービス停止が発生します。この記事では、この問題の原因を詳細に分析し、より安定した堅牢なソケットサービスを構築するのに役立つ実用的な対応戦略を提供します。
UNIXのようなシステムでは、各プロセスには、ファイル、ソケット、パイプラインなどのリソースへの参照を管理するファイル記述テーブルがあります。ファイル記述子は整数であり、システムは各オープンリソースに一意のFDを割り当てます。ネットワークプログラミングの場合、クライアントが接続するたびに新しいFDが作成されます。
デフォルトでは、Linuxシステムが単一のプロセスで開くことができるFDの数は限られています(通常は1024)。非常に同時のソケットサービスを実行すると、接続リソースが適切に管理されていない場合、FDは非常に迅速に使い果たされます。
PHPでソケットプログラミングを使用する場合、 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);
if ($client) {
// クライアント接続を処理します
}
}
socket_accept()が正常に実行されるたびに、新しいクライアントソケットリソースが作成され、FDが占有されます。クライアントが時間内に閉じられていない場合、または異常な条件下でリリースされない場合、FDSの数は増加し続け、最終的にsocket_accept()が誤ってfalseを返し、エラーを報告します。
まず、システムで許可されているファイル記述子の最大数を確認し、適切に増やします。
ulimit -n 65535
また、編集/etc/security/limits.confを使用して設定を維持することもできます。
www-data soft nofile 65535
www-data hard nofile 65535
ブロックSocket_Accept()は、接続を待っている間にスレッドを掛けます。システムFDが使い果たされている場合、プログラムは失速します。 socket_set_nonblock()を使用して、新しい接続が現在受け入れられていない場合でも、プログラムを実行し続けます。
socket_set_nonblock($socket);
socket_accept()がリソースのデッドループを回避できない後、遅延再試行ロジックを追加できます。
$client = @socket_accept($socket);
if ($client === false) {
usleep(100000); // 待って 100ms もう一度やり直してください
continue;
}
クライアント接続がデータ転送を完了すると、接続を明示的に閉じ、リソースをリリースする必要があります。
socket_close($client);
オフにしないと、FDは常に消費されます。すべてのクライアント接続を配列に管理し、定期的にアクティブなステータスを確認し、時間内に無効な接続をクリーンアップすることをお勧めします。
Simple while + socket_accept()と比較して、 socket_select()を使用してイベント駆動型モデルを実装することをお勧めします。 Select() 、複数の接続を同時に聴き、イベントが発生したときに接続を処理して、リソースの不必要な無駄を回避できます。
$read = [$socket];
$write = $except = [];
if (socket_select($read, $write, $except, 0) > 0) {
if (in_array($socket, $read)) {
$client = socket_accept($socket);
if ($client) {
socket_set_nonblock($client);
$clients[] = $client;
}
}
}
高い並行性シナリオでは、接続の最大数を制限することにより、FDが同時に入力されるのを防ぐことができます。
$maxClients = 1000;
if (count($clients) >= $maxClients) {
socket_close($client); // 新しい接続を拒否します
continue;
}
また、Nginxまたは同様のミドルウェアを介してフロントエンド接続の数を制限して、サーバーの圧力を軽減することもできます。例えば:
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
server {
listen 80;
server_name m66.net;
location / {
limit_conn conn_limit_per_ip 20;
proxy_pass http://localhost:8080;
}
}
FD排気はソケットサービスの一般的な落とし穴の1つですが、関連する原則を習得し、非ブロッキングモード、合理的な接続閉鎖、接続番号制御、その他の方法を通じてそれらを扱う限り、システムの安定性と可用性を大幅に改善できます。堅牢なソケットサービスを構築することは、実行できるコードを作成するだけでなく、さらに重要なことに、システムリソースを合理的に管理し、潜在的なリスクを防ぐことです。
Socket Projectでは、 socket_accept() falseまたは "Openファイルが多すぎる]のエラーに直面している場合、上記の方向からトラブルシューティングおよび最適化して、効率的で信頼できるサーバープログラムを作成することもできます。