Position actuelle: Accueil> Derniers articles> Comment éviter le problème de l'épuisement des descripteurs de fichiers (FD) lors de l'utilisation de la fonction socket_accept ()?

Comment éviter le problème de l'épuisement des descripteurs de fichiers (FD) lors de l'utilisation de la fonction socket_accept ()?

M66 2025-06-04

Lors de l'écriture d'applications de serveur basées sur des socket à l'aide de la fonction Socket_Accept () de PHP, les développeurs rencontrent souvent un mal de tête: cela fait généralement défaut que le serveur accepte les nouvelles connexions client, provoquant des pannes de service. Cet article analysera les causes de ce problème en profondeur et fournira des stratégies de réponse pratiques pour vous aider à créer un service de socket plus stable et robuste.

1. Qu'est-ce qu'un descripteur de fichiers (FD)?

Dans les systèmes de type UNIX, chaque processus dispose d'un tableau de descripteur de fichiers qui gère des références à des ressources telles que des fichiers, des prises, des pipelines, etc. Un descripteur de fichiers est un entier, et le système attribue une FD unique à chaque ressource ouverte. Pour la programmation réseau, une nouvelle FD est créée à chaque fois que le client se connecte.

Par défaut, le nombre de FDS qu'un système Linux peut ouvrir pour un seul processus est limité (généralement 1024). Lorsque vous exécutez un service de prise très simultané, si les ressources de connexion ne sont pas correctement gérées, la FD sera épuisée très rapidement.

2. Le mécanisme de travail de socket_accept ()

Lorsque vous utilisez la programmation de socket dans PHP, socket_accept () est une fonction clé utilisée pour accepter les connexions client. L'utilisation de base est la suivante:

 $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) {
        // Gérer les connexions du client
    }
}

Chaque fois que socket_accept () est exécuté avec succès, une nouvelle ressource de socket client est créée, qui occupera une FD. Si le client n'est pas fermé dans le temps ou n'est pas libéré dans des conditions anormales, le nombre de FD continuera d'augmenter, ce qui conduira éventuellement à Socket_Accept () Retour False et à signaler une erreur.

3. Comment éviter l'épuisement FD?

1. Réglez la limite supérieure FD

Tout d'abord, confirmez et augmentez de manière appropriée le nombre maximum de descripteurs de fichiers autorisés par le système:

 ulimit -n 65535

Vous pouvez également persister le cadre en modifiant /etc/security/limits.conf :

 www-data soft nofile 65535
www-data hard nofile 65535

2. Définissez la prise sur le mode non bloquant

Le blocage socket_accept () accrochera le fil en attendant une connexion. Si le système FD est épuisé, le programme calendra. Utilisez socket_set_nonblock () pour maintenir le programme en cours d'exécution même si la nouvelle connexion n'est actuellement pas acceptable.

 socket_set_nonblock($socket);

Vous pouvez ajouter de la logique de réessayer de retard après que socket_accept () ne parvient pas à éviter la boucle morte des ressources:

 $client = @socket_accept($socket);
if ($client === false) {
    usleep(100000); // attendez 100ms Essayer à nouveau
    continue;
}

3. Fermer activement la connexion non valide

Lorsqu'une connexion client termine le transfert de données, la connexion doit être explicitement fermée et que les ressources doivent être publiées:

 socket_close($client);

S'il n'est pas désactivé, la FD sera consommée tout le temps. Il est recommandé de gérer toutes les connexions du client dans un tableau, de vérifier périodiquement son état actif et de nettoyer les connexions non valides dans le temps.

4. Utilisez le mécanisme de multiplexage de sélection ()

Par rapport à la simple while + socket_accept () , il est recommandé d'utiliser socket_select () pour implémenter le modèle axé sur les événements. SELECT () , vous pouvez écouter plusieurs connexions en même temps et traiter la connexion lorsqu'un événement se produit, en évitant le gaspillage de ressources inutile.

 $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;
        }
    }
}

5. Rejoignez le nombre de limites de connexions

Dans les scénarios de concurrence élevés, vous pouvez empêcher la FD d'être remplie simultanément en limitant le nombre maximum de connexions:

 $maxClients = 1000;

if (count($clients) >= $maxClients) {
    socket_close($client); // Rejeter une nouvelle connexion
    continue;
}

Vous pouvez également limiter le nombre de connexions frontales via NGINX ou middleware similaire pour réduire la pression du serveur. Par exemple:

 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;
    }
}

4. Résumé

L'épuisement FD est l'un des pièges courants dans les services de socket, mais tant que nous maîtrisons les principes pertinents et les traitons en mode non bloquant, la fermeture de la connexion raisonnable, le contrôle des nombres de connexions et d'autres méthodes, nous pouvons considérablement améliorer la stabilité et la disponibilité du système. La construction d'un service de prise robuste ne consiste pas seulement à écrire du code qui peut s'exécuter, mais plus important encore, il s'agit de gérer rationnellement les ressources système et de prévenir les risques potentiels.

Dans votre projet de socket, si vous êtes confronté à l'erreur de socket_accept () renvoyant false ou "trop ​​de fichiers ouverts", vous pourriez également dépanner et optimiser les instructions ci-dessus pour créer un programme de serveur efficace et fiable.