Lors de la création d'une application de serveur basée sur Socket à l'aide de PHP, socket_accept () est l'une des fonctions principales qui accepte les demandes de connexion du client. Cependant, si la gestion de la connexion n'est pas gérée correctement, il est facile de provoquer une fuite de ressources et même une terminaison de service anormale. Cet article explorera comment ces connexions peuvent être établies pour garantir que les ressources peuvent être publiées dans le temps et pour maintenir la stabilité et la fiabilité des services.
Avant de comprendre un arrêt élégant, regardez un exemple de serveur de socket de base:
$address = '0.0.0.0';
$port = 12345;
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, $address, $port);
socket_listen($serverSocket);
while (true) {
$clientSocket = socket_accept($serverSocket);
// Réponse simple
socket_write($clientSocket, "Hello client!\n");
socket_close($clientSocket);
}
Dans cet exemple, le serveur continue d'exécuter, reçoit la connexion et répond. Bien que le code puisse s'exécuter, si la connexion est fermée et que l'exception est traitée de manière déraisonnable pendant une concurrence élevée ou à long terme, les problèmes suivants peuvent survenir:
Fuite du descripteur de fichiers (dépassement des limites de ressources système)
La connexion n'est pas fermée, provoquant l'accumulation de temps_wait
Le processus est obligé de se terminer (s'il est rencontré Sigint)
PHP's socket_accept () est une fonction de blocage , ce qui signifie qu'il n'attendra aucune demande de connexion. Ce comportement empêche l'arrêt normal du processus lors de la sortie du service, du redémarrage ou de la fin. Si la ressource n'est pas publiée correctement, elle peut entraîner:
socket_close () non exécuté : la connexion n'est pas fermée, les ressources système ne sont pas publiées.
Le script sort anormalement : entraîne le nettoyage de la connexion créée, laissant derrière elle une prise zombie.
Interruption du signal (comme Ctrl + C): Aucune conséquence n'est traitée.
Bien que la série Socket_ * fournit un contrôle de niveau inférieur, Stream_Socket_Server () de PHP est plus facile à implémenter le contrôle du délai et le comportement non bloquant, adapté pour simplifier le code.
Cependant, si vous insistez sur l'utilisation de Socket_ * , les méthodes suivantes peuvent également réaliser une fermeture élégante.
Socket_set_nonblock () peut être évité de bloquer socket_accept () :
socket_set_nonblock($serverSocket);
De cette façon, nous pouvons utiliser une boucle pour vérifier s'il y a une connexion et écouter le signal de terminaison:
declare(ticks = 1);
$running = true;
pcntl_signal(SIGINT, function() use (&$running) {
echo "capturer SIGINT,Se préparer à quitter...\n";
$running = false;
});
while ($running) {
$clientSocket = @socket_accept($serverSocket);
if ($clientSocket !== false) {
socket_write($clientSocket, "Hello client!\n");
socket_close($clientSocket);
} else {
usleep(100000); // éviter CPU Occupation trop élevée
}
}
socket_close($serverSocket);
Si vous avez besoin d'écouter plusieurs sockets, vous pouvez combiner socket_select () pour implémenter le modèle axé sur les événements:
$readSockets = [$serverSocket];
$write = $except = null;
if (socket_select($readSockets, $write, $except, 1) > 0) {
foreach ($readSockets as $sock) {
if ($sock === $serverSocket) {
$clientSocket = socket_accept($serverSocket);
if ($clientSocket !== false) {
socket_write($clientSocket, "Hi\n");
socket_close($clientSocket);
}
}
}
}
Socket_Select () peut définir un délai d'expiration ou répondre aux signaux d'interruption dans le temps, ce qui convient à la mise en œuvre de programmes de serveurs plus robustes.
Assurez-vous que chaque ressource obtenue par socket_create () et socket_accept () est appariée et exécutée socket_close () . Un cycle de vie de service de douille bien structuré est le suivant:
Initialiser la prise
Définir le non-bloquant / Sélectionner
Recevoir la connexion dans la boucle principale et les demandes de processus
Capturez le signal de terminaison et sortez de la boucle
Nettoyez toutes les connexions ouvertes
Enfin, fermez la prise d'écoute
Voici un exemple complet qui met en œuvre la capture du signal, le mode non bloquant et le nettoyage des ressources:
declare(ticks = 1);
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, '0.0.0.0', 8000);
socket_listen($serverSocket);
socket_set_nonblock($serverSocket);
$running = true;
pcntl_signal(SIGINT, function() use (&$running) {
echo "Reçu un signal de résiliation,Préparez-vous à fermer le service...\n";
$running = false;
});
while ($running) {
$clientSocket = @socket_accept($serverSocket);
if ($clientSocket) {
socket_write($clientSocket, "Bienvenue à visiter m66.net!\n");
socket_close($clientSocket);
} else {
usleep(100000);
}
}
socket_close($serverSocket);
echo "Le service a été élégamment fermé。\n";
Bien que l'interface de programmation de socket fournie par PHP soit puissante, elle apporte également la complexité de la gestion des ressources. En définissant le mode non bloquant, en écoutant des signaux de terminaison, en utilisant socket_select () et d'autres méthodes, "un arrêt élégant" peut être réalisé en PHP, évitant efficacement les problèmes de fuite de ressources et de terminaison anormale. Ces méthodes sont particulièrement importantes lors de la construction de services de longue durée.
N'oubliez pas: relâchez toujours toutes les ressources que vous ouvrez, y compris les prises.