Maison > Article > développement back-end > Apprentissage des sockets PHP : vous amène à créer un simple serveur de sockets
Cet article vous donnera une première exploration des sockets PHP. Découvrons les sockets en créant un simple serveur de socket. J'espère que cela vous sera utile !
Le nom chinois du socket est appelé socket. Ce genre de chose est "l'encapsulation" de TCP/IP. En réalité, le réseau ne comporte en réalité que quatre couches, de haut en bas : la couche application, la couche transport, la couche réseau et la couche liaison de données. Le protocole http le plus couramment utilisé est un protocole appartenant à la couche application, et le socket peut être simplement et grossièrement compris comme un élément de la couche transport. Si c'est encore difficile à comprendre, alors ajoutez tcp://218.221.11.23:9999 plus grossièrement, vous le voyez ? Il s'agit d'un socket TCP.
Socket nous donne la possibilité de contrôler la couche de transport et la couche réseau, obtenant ainsi des performances plus élevées et une efficacité plus élevée. La programmation Socket est la solution la plus couramment utilisée et la plus mature pour résoudre les serveurs réseau à haute concurrence. Tout programmeur de serveur doit maîtriser les compétences liées à la programmation de sockets.
En php, il existe deux ensembles de fonctions qui peuvent contrôler les sockets. L'une est la série de fonctions socket_ et l'autre est la série de fonctions stream_. Socket_ est une implémentation obtenue en copiant directement le socket en langage C, tandis que le système stream_ utilise PHP pour l'encapsuler en utilisant le concept de stream. Ce qui suit utilise les fonctions de la série socket_* pour démarrer simplement cette série d'articles.
Créez d'abord le serveur socket le plus simple :
<?php $host = '0.0.0.0'; $port = 9999; // 创建一个tcp socket $listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); // 将socket bind到IP:port上 socket_bind( $listen_socket, $host, $port ); // 开始监听socket socket_listen( $listen_socket ); // 进入while循环,不用担心死循环死机,因为程序将会阻塞在下面的socket_accept()函数上 while( true ){ // 此处将会阻塞住,一直到有客户端来连接服务器。阻塞状态的进程是不会占据CPU的 // 所以你不用担心while循环会将机器拖垮,不会的 $connection_socket = socket_accept( $listen_socket ); // 向客户端发送一个helloworld $msg = "helloworld\r\n"; socket_write( $connection_socket, $msg, strlen( $msg ) ); socket_close( $connection_socket ); } socket_close( $listen_socket );
Enregistrez le fichier sous server.php, puis exécutez php server.php pour l'exécuter. Nous pouvons utiliser telnet sur le client. Ouvrez un autre terminal et exécutez telnet 127.0.0.1 9999 et appuyez sur Entrée. Les résultats d'exécution sont les suivants :
Une brève analyse du code ci-dessus pour illustrer le processus du serveur socket TCP :
Dans le cas ci-dessus, il y a deux gros défauts :
Après avoir analysé les problèmes ci-dessus, j'ai pensé au multi-processus mentionné plus tôt. Ensuite, nous pouvons créer un processus enfant pour gérer la demande du client après qu'acpet ait reçu une demande, de sorte qu'après avoir accepté le deuxième client, nous puissions créer un enfant. processus pour traiter la demande du deuxième client. Le problème ne serait-il pas résolu ? D'ACCORD! Montrons le code :
<?php $host = '0.0.0.0'; $port = 9999; // 创建一个tcp socket $listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); // 将socket bind到IP:port上 socket_bind( $listen_socket, $host, $port ); // 开始监听socket socket_listen( $listen_socket ); // 进入while循环,不用担心死循环死机,因为程序将会阻塞在下面的socket_accept()函数上 while( true ){ // 此处将会阻塞住,一直到有客户端来连接服务器。阻塞状态的进程是不会占据CPU的 // 所以你不用担心while循环会将机器拖垮,不会的 $connection_socket = socket_accept( $listen_socket ); // 当accept了新的客户端连接后,就fork出一个子进程专门处理 $pid = pcntl_fork(); // 在子进程中处理当前连接的请求业务 if( 0 == $pid ){ // 向客户端发送一个helloworld $msg = "helloworld\r\n"; socket_write( $connection_socket, $msg, strlen( $msg ) ); // 休眠5秒钟,可以用来观察时候可以同时为多个客户端提供服务 echo time().' : a new client'.PHP_EOL; sleep( 5 ); socket_close( $connection_socket ); exit; } } socket_close( $listen_socket );
Enregistrez le code sous server.php, puis exécutez php server.php Le client utilise toujours telnet 127.0.0.1 9999, mais cette fois nous ouvrons deux terminaux pour exécuter telnet. Il est important de noter qu’après la connexion du premier client, le deuxième client peut également se connecter. Les résultats en cours sont les suivants :
En recevant l'horodatage de la demande du client, vous pouvez voir que le serveur peut désormais servir N clients en même temps. Mais alors réfléchissez-y, et si 10 000 clients venaient en faire la demande ? À ce stade, le serveur déboursera 10 000 processus enfants pour gérer chaque connexion client, ce qui tuera des personnes. Fork lui-même est un appel système qui gaspille les ressources du système. 10 000 forks suffisent à provoquer le crash du système. Même si le système peut supporter 1 000 forks, les 1 000 processus enfants qui sortent du fork suffisent à consommer un pot de mémoire système. . En fin de compte, est-ce que ça va ? Le processus enfant qui est facile à débourser sera fermé après le traitement du client actuel, et il devra être à nouveau bifurqué pour la prochaine requête. C'est en soi un gaspillage et n'est pas conforme. aux valeurs socialistes dominantes. En cas d’attaque malveillante, le nombre de forks du système augmentera linéairement jusqu’à ce que le système tombe en panne.
Nous proposons donc une fois de plus une solution améliorée. Nous pouvons estimer le volume d'activité, puis créer un nombre fixe de processus enfants au démarrage du service. Chaque processus enfant est dans une boucle infinie et bloqué lors de l'acceptation. Lorsqu'une connexion client est établie, la demande du client est traitée après le traitement. est terminée, la connexion est seulement fermée mais pas détruite, mais continue d'attendre la prochaine demande du client. De cette façon, cela évite non seulement l'énorme gaspillage de ressources causé par les forks répétés du processus, mais protège également le système contre les plantages dus à des forks infinis grâce à un nombre fixe de processus enfants.
<?php $host = '0.0.0.0'; $port = 9999; // 创建一个tcp socket $listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); // 将socket bind到IP:port上 socket_bind( $listen_socket, $host, $port ); // 开始监听socket socket_listen( $listen_socket ); // 给主进程换个名字 cli_set_process_title( 'phpserver master process' ); // 按照数量fork出固定个数子进程 for( $i = 1; $i <= 10; $i++ ){ $pid = pcntl_fork(); if( 0 == $pid ){ cli_set_process_title( 'phpserver worker process' ); while( true ){ $conn_socket = socket_accept( $listen_socket ); $msg = "helloworld\r\n"; socket_write( $conn_socket, $msg, strlen( $msg ) ); socket_close( $conn_socket ); } } } // 主进程不可以退出,代码演示比较粗暴,为了不保证退出直接走while循环,休眠一秒钟 // 实际上,主进程真正该做的应该是收集子进程pid,监控各个子进程的状态等等 while( true ){ sleep( 1 ); } socket_close( $connection_socket );
Enregistrez le fichier sous server.php et exécutez php server.php, puis utilisez ps -ef | grep phpserver | grep -v grep pour vérifier l'état du processus du serveur :
Vous pouvez voir que le processus maître existe. De plus, il y a 10 sous-processus en attente de service. Il peut fournir des services à 10 clients en même temps. Essayons-le via telnet 127.0.0.1 9999. Le résultat actuel est le suivant :
D'accord, la nouvelle série de voyages de php commence par une simple introduction ! Le prochain article décrira quelques bases théoriques plus approfondies.
Apprentissage recommandé : "Tutoriel vidéo PHP"
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!