Travailler avec les sockets

Cet article va tenter de vous éclairer sur l'utilisation des sockets en PHP. Je remercie Jérôme et Cyberzoïde pour leur aide précieuse et leurs corrections apportées à ce tuto.

Article lu   fois.

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

1. Introduction

Cet article s'adresse aux personnes ayant déjà un minimum de connaissance en PHP.

Nous allons aborder les points suivants:

  • Les principales fonctions liées aux sockets
  • La création d'un protocole de communication
  • La réalisation d'un script qui sera le serveur d'écoute
  • La réalisation d'un script qui sera le client, qui se connectera et enverra des données sur la socket

Pour pouvoir utiliser les fonctions liées aux sockets, il faut :

  • activer l'extension php_sockets.dll (Windows:retirer le ; devant l'extension, Linux: configurer PHP avec --enable-sockets)
  • Disposer de l'environnement CLI de php (installé d'office à partir de PHP 4.3.0)

2. Définition d'une socket

Une socket est un identifiant unique représentant une adresse sur le réseau. Des processus peuvent s'y connecter pour y envoyer des données ou pour en recevoir. Les processus devront adopter un protocole de communication afin d'assurer un échange de données cohérent. L'adresse de la socket est spécifiée par le nom de l'hôte sur lequel on la crée et le numéro de port. Dans les exemples qui vont suivre, nous allons nous appuyer sur le protocole TCP-IP qui se chargera d'assurer le transport des données (paquets) entre les processus serveur et client.

Dans ce contexte, le serveur est le processus qui écoute toute nouvelle connexion de client et effectue la récupération des données. Le client est donc le processus qui va tenter de se connecter au serveur et de lui envoyer des données. Dans l'exemple qui va suivre, le serveur et le client sont des scripts PHP mais en réalité, grâce aux sockets, vous pouvez faire communiquer des langages totalement différents pour peu qu'ils adoptent le même protocole de communication.

3. Dans quel contexte est-il judicieux d'utiliser les sockets?

  • La communication en temps réel
  • Serveur de chat
  • Serveur d'authentification
  • Toute application réseau

4. Définition du protocole de communication

Comme je l'ai dit ci-dessus, nous allons utiliser le protocole TCP-IP qui se chargera de transporter les paquets de données entre le client et le serveur. TCP-IP est dans ce contexte un bon choix puisqu'il s'assure que tous les paquets transmis par le client soient entièrement restitués au serveur dans le bon ordre.

Il n'en reste pas moins que nous allons devoir "inventer" notre propre protocole de communication afin de s'assurer que les paquets transmis du client vers le serveur soient compréhensibles pour celui-ci. Le protocole n'a donc d'autre but que de faire en sorte que le serveur "comprenne" ce que le client lui envoie et éventuellement, rejette le client en question si celui-ci ne respecte pas le protocole établi. Les méthodes généralement utilisées pour transmettre les paquets sont les suivantes:

  • Envoi d'un en-tête suivi des données d'une longueur variable
  • Envoi d'un en-tête suivi des données d'une longueur fixe

L'en-tête fournit des informations par rapport aux données qui suivent. Elle est généralement de longueur fixe.

Schéma d'un protocole ayant des données de longueur fixe
En-tête Donnée 1 (20 octets) Donnée 2 (30 octets)
Schéma d'un protocole ayant des données de longueur variable
En-tête Donnée 1 (?) Donnée 2 (?)

Dans le premier schéma, la situation est assez claire puisque le client enverra toujours des données d'une longueur fixe. L'avantage étant qu'il n'est pas nécessaire de calculer la longueur réelle des données, on définit en fait une longueur maximale pour chaque type de donnée. Le désavantage est que si la longueur réelle est inférieure à la limite définie, on transfère quand même la totalité.

Dans le deuxième schéma, le client devra calculer la longueur des données transmises et l'indiquer dans l'en-tête. De la sorte, lorsque le serveur lira l'en-tête, il saura le nombre d'octets qu'il doit lire pour récupérer tout le paquet. L'avantage est que la longueur réelle est transmise et que nous ne gapillerons donc pas d'espace. Le désavantage est que le client doit calculer cette longueur et que le serveur doit effectuer un traitement pour ne récupérer que ce que l'en-tête lui dit de récupérer. C'est donc un peu plus ardu à développer.

Il est à noter que le choix d'un protocole n'est indispensable que si l'on traite des données structurées. Par exemple, si vous décidez de ne transmettre que du texte ne correspondant pas à une information précise entre le client et le serveur, il n'est pas nécessaire de définir un protocole. Dans ce cas, vous pourriez simplement vous contenter de lire les données reçues jusqu'à ce que vous receviez le caractère de fin de communication du client.

Dans l'exemple que nous allons étudier, nous allons voir comment les données structurées sont transmises du client vers le serveur de chat. Ces données sont structurées comme suit:

  • Pseudo
  • Message

Dans ce cas, nous sommes obligés de définir un protocole car on ne peut pas se permettre de recevoir les données en vrac, sous peine de ne pas pouvoir les traiter. Par traitement, j'entends la retransmission du message envoyé par le client à tous les autres clients connectés et à lui-même. J'ai donc opté pour le premier schéma, à savoir, un en-tête de longueur fixe spécifiant la longueur des données qui la suivent. Dans le cas présent, nous savons donc qu'un paquet correspondra à 2 données.

Schéma du protocole que nous allons utiliser
En-tête Pseudo Message
10057 developpez L'équipe de DVP est heureuse que vous lisiez cet article!

Nous enverrons TOUJOURS un en-tête de 5 octets. Cet en-tête se divise en 2 parties. Dans l'exemple ci-dessus, les parties correspondent à:

  • Partie 1 -> 10 -> longueur de la donnée "Pseudo"
  • Partie 2 -> 057 soit 57 -> longueur du message envoyé par cette personne

Comme vous pouvez le constater, 2 octets étant alloués pour le pseudo, la longueur du pseudo ne pourrait en aucun cas dépasser 99 octets, ce qui est largement suffisant. J'ai toutefois imposé une limitation de 15 caractères dans le script client. Trois octets sont alloués à la longueur du message, ce qui a pour effet que la longueur du message ne peut dépasser 999 octets. Afin de TOUJOURS envoyer un en-tête de 5 octets, nous allons coder celle-ci.

Si la longueur du pseudo est inférieure à 10 octets, nous ajouterons un 0 devant celle-ci. Lorsque le serveur récupèrera le paquet, il lira d'abord l'en-tête (5 octets), il ne récupèrera que la partie significative des parties de l'en-tête. En découpant l'en-tête, il pourra déterminer qu'il doit lire en tout 67 octets (10 + 57 dans ce cas-ci) pour récupérer la totalité du paquet. Il lira ce paquet et ensuite le découpera pour isoler chaque information et appliquer un traitement spécifique, à savoir, redistribuer ce message à tous les clients. La manière de transmettre les informations sera donc toujours "en-tête->données->en-tête->données..."

5. Brève description des principales fonctions que nous allons utiliser

Fonction Description
socket_create Crée un point de communication et le retourne sous forme de ressource
socket_bind Tente de s'accaparer le point de communication se trouvant sur l'hôte spécifié et le numéro de port. Attention: le numéro de port doit être libre
socket_listen Attend de nouvelles données sur la socket.
socket_accept Bloque l'exécution du code jusqu'à ce qu'une connexion cliente soit établie. Lorsqu'une connexion cliente est établie, socket_accept l'accepte et exécute le code qui suit
socket_read Permet de lire les données qui ont été écrite sur une socket.
socket_write Permet d'écrire des données sur une socket.
socket_connect Permet de se connecter à une socket existante
socket_getpeername Permet de récupérer l'IP du client qui se connecte ainsi que le port
socket_last_error Renvoie le code de la dernière erreur
socket_strerror Renvoie un message d'erreur lisible, on lui passe en paramètre le code retourné par socket_last_error
socket_close Ferme la ressource allouée à la socket

Note: les fonctions ci-dessus sont expérimentales.

6. Scripts

Il y a en tout sept scripts qui sont utilisés pour faire fonctionner ce chat. Nous n'allons parcourir que les scripts les plus importants, à savoir le script du serveur et les scripts du client. Je me suis contenté de créer un chat assez basique car le but est de vous montrer comment utiliser des sockets, pas de faire une nouvelle version MIRC :)

Ce chat comprend néanmoins les fonctionnalités suivantes:

  • Gestion du nombre maximum de connexions clientes simultanées
  • Affichage d'une liste contenant tous les clients connectés
  • Avertissement à tous les connectés lorsque quelqu'un rejoint le chat + MAJ liste
  • Avertissement à tous les connectés lorsque quelqu'un quitte le chat + MAJ liste
  • Gestion d'une utilisation unique d'un pseudo, si quelqu'un est déjà connecté avec un pseudo, la connexion est refusée
  • Réalisation d'un script permettant de stopper le serveur de manière "propre" et envoyant un message à tous les connectés notifiant l'arrêt du serveur
  • "Wrapping" du message envoyé par un client
  • Une gestion d'information log qui peut-être, soit dirigée vers la sortie standard (stdo), soit vers un fichier

Ce chat nécessite que le client accepte le javascript. Il a été testé sous la configuration suivante:

  • IE6 et Mozilla/5.0.
  • PHP 4.3.6
  • Windows XP pro SP1 et Windows 2000 Pro

Il n'a pas été testé sous Linux mais devrait nénanmoins tourner moyennant sans doute quelques modifications au niveau de la configuration de Linux.

6.1. Quelques screen shots

Le serveur

Image non disponible

L'interface client

Image non disponible

6.2. Architecture

Image non disponible

Dans notre cas de figure, il y a deux types de clients. Les clients de type "navigateur" qui envoient un en-tête de type GET au serveur. Les clients php qui envoient les messages que les utilisateurs encodent. Lorsque le serveur reçoit un en-tête de type GET, il sait qu'il s'agit d'une nouvelle connexion. Lorsque le serveur reçoit un en-tête codée décrivant un message, il redistribue ce message à tous les clients de type "navigateurs".

Diagramme des séquences sur l'utilisation des fonctions.

Image non disponible

6.3. Fonctionnement du chat

Cette section a pour but de vous expliquer le fonctionnement du chat afin que vous compreniez la logique de développement. Comme je l'ai dit un peu plus haut, il y a deux types de clients et un serveur. Lorsqu'un client de type navigateur établit une connexion sur le serveur, il le fait en pointant vers la socket serveur:

http://localhost:9814?Pseudo=....par exemple

Cette connexion est établie par le script d'entrée dans le chat décrit en section 6.6. Lorsque le navigateur est connecté, le serveur détecte cette nouvelle connexion, vérifie le pseudo reçu en paramètre et accepte cette connexion. Il l'ajoute dans un tableau de connexions qui contient toutes les connexions de type "navigateur". Lorsqu'un client envoie un message, il le fait via le script send_message.php qui est executé à chaque fois que le client envoie le formulaire. Ce script crée donc une nouvelle connexion qui sert uniquement à envoyer le message au serveur et qui est fermée immédiatement. Le serveur ne se soucie donc plus de cette connexion. Ensuite, le serveur récupère le message envoyé et l'envoie à son tour à toutes les connexions de type "navigateur". C'est dans ce processus de distribution de message que le serveur détecte si un client "navigateur" s'est déconnecté. On estime que le client s'est déconnecté si on arrive pas à lui envoyer le message, dans ce cas, on actualise le tableau des connexions en enlevant cette ressource. Cette méthode a donc un inconvénient puisque on ne détecte la déconnexion d'un client que lorsqu'un nouveau message est envoyé ou lorsqu'une nouvelle connexion est établie par un autre client.

Un client de type navigateur reste connecté tant que la fenêtre n'a pas été fermée par l'utilisateur ou après une durée d'inactivité assez longue. Ce dont vous devez vous rendre compte, c'est que lorsque une connexion de type navigateur est établie, nous n'intervenons plus du tout sur cette connexion, si ce n'est pour lui envoyer des messages via le serveur.

6.4. Script du serveur

 
Sélectionnez

 
<?
/*
Presque toutes les propriétés de la classe ont une valeur par défaut
vous pouvez bien entendu les changer dans le script start_chat.php en
spécifiant l'instance de l'objet suivi de la propriété $objet->propriété=valeur.
*/
class Chat_Server{
//Nombre de connexions concurrentes au maximum
var $max_clients=10;
//Un tableau qui contiendra les ID de sockets de tous les clients connectés
var $clients=array();
//La socket "maître" sur laquelle le serveur écoute
var $socket=null;
//Contiendra l'id de chaque nouvelle connexion
var $client=null;
//Contient un message à afficher lorsqu'une connexion est refusée
var $denied;
//0=afficher les infos sur l'écran, 1=enregistrer les log dans un fichier
var $log=0;
//Nom du fichier log  stocker les infos
var $logfile='ChatServerLog.log';
//Ressource du fichier log
var $fp_log;
//Contient l'en-tête html à envoyer à chaque nouveau client
var $html;
//Si un pseudo est déjà pris, envoyer ce message au client avant de refuser sa connexion
var $Already_In_use;
//Type d'info que l'on envoie au(x) client(s) 
 
//1->MAJ liste connectés+message 2->envoi d'un message 3->ne pas reformatter le message
var $write_type=0;
//Méthode qui démarre le serveur
function Start($adress,$port){
  $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//on lie la ressource sur laquelle le serveur va écouter
  socket_bind($this->socket, $address, $port) or die($this->destroy(null));
//On prépare l'écoute
  socket_listen($this->socket);
//Boucle infinie car le serveur ne doit s'arrêter que si on lui demande
  while(true){
//Le code se bloque jusqu'à ce qu'une nouvelle connexion cliente est établie
	$this->client=socket_accept($this->socket);
//Lors d'une connexion, le code reprend ici, il est temps de lire ce qu'on nous envoie
	$this->read_write();
  }
}
 
//Cette méthode lit les données reçues d'un client et les redistribue
function read_write(){
//L'en-tête fait 5 bytes, donc on lit 5 bytes et on vérifie s'il s'agit d'une connexion
//cliente ou si il s'agit d'un envoi de message ou encore s'il s'agit de stopper le serveur
  $input = socket_read($this->client, 5);
//11111 est le signal d'arrêt du serveur, vous pourriez en définir un autre
  if($input=='11111'){
	$this->write_type=3;
//On envoie un message à tous les clients, notifiant l'arrêt du serveur
	$this->Write_To_Clients("<script language=\"javascript\">alert('Le serveur a été arrêté')</script>");
// Temporisation de 3 secondes pour que les clients aient le temps de lire le message
	sleep(3);
//On appelle la méthode qui arrête le serveur proprement
	$this->Destroy(null);
  }
 
//Si le mot clé "get" figure dans l'en-tête, c'est qu'il s'agit d'une nouvelle connexion
  if(substr_count($input,'GET')>0){ 
//Si le nombre maximum autorisé de connexions n'est pas atteint
    if($this->max_clients > count($this->clients)){
//On lit les 50 octets suivants pour récupérer le pseudo
	  $nick=socket_read($this->client,50);
	  if(substr_count($nick,'Pseudo')==0){
//Accès refusé car pseudo invalide dans le cas  le client tape directement l'adresse dans la barre d'URL
         socket_close($this->client);
         return;
      }
//On récupère le pseudo qui se trouve après '?Pseudo=' et fait 15 caractères
	  $nick=trim(substr($nick,(strpos($nick,'=')+1),15));
//On contrôle la validité du pseudo	  
	  if(substr_count($nick,' ')>0 || $nick==null){
	    socket_close($this->client);
	    return;
	  }
//On tente d'obtenir l'IP du client.
	  @socket_getpeername($this->client,&$adress,&$port);
//On vérifie que le pseudo n'est pas déjà utilisé
	  if($this->clients[$nick]==null){
//On ajoute la connexion au tableau des connexions
		$this->clients[$nick]=$this->client;
//On avertit les autres que ce client vient de se connecter
		$this->Write_Connected();
		$this->write_type=1;
//On met à jour la liste de tous les connectés chez tous les clients
		$this->Write_To_Clients($nick.':'.$adress);
//On enregistre ou affiche qu'une nouvelle connexion a été établie
		$this->Logging('Nouvelle connexion client : '.$adress.':'.$port);
	  }
	  else{
//Si le pseudo est déjà utilisé, on refuse la connexion
	   $this->write_type=3;
	   @socket_write($this->client,$this->html.$this->Already_In_use,(strlen($this->Already_In_use)+strlen($this->html)));
	   sleep(1);
	   @socket_close($this->client);
	  }
	}
	else{        
//Connexion refusée si le nombre maximal de connexions est atteint
	  @socket_write($this->client,$this->denied,strlen($this->denied));
	  sleep(1);
	  socket_getpeername($this->client,&$adress,&$port);
	  $this->Logging('Client '.$this->client.' : '.$adress.':'.$port.' a eu un accès refusé');
	  @socket_close($this->client);
	}
 
  }
  else{
/*Si c'est pas un client de type get, alors c'est un client qui tente d'écrire un message
donc on récupère le paquet grâce aux parties numériques significatives de l'en-tête*/  
	$paquet=socket_read($this->client,intval(substr($input,0,2))+intval(substr($input,2,3)));
	$pseudo=substr($paquet,0,intval(substr($input,0,2)));
	$message=substr($paquet,intval(substr($input,0,2)),intval(substr($input,2,3)));
//On ajoute des br dans le message
	$this->Wrap_Message(&$message);                
	$full_client_msg="<font color='#FF0000'> [$pseudo a écrit:]<font><font color='#AF00AF'>$message</font><br>";
	$this->write_type=2;
//On envoie le message à tous les clients connectés
	$this->Write_To_Clients($full_client_msg);
  }
}
//Cette méthode distribue les messages à tous les connectés
function Write_To_Clients($msg){
  reset($this->clients);
  if($this->write_type==1){
	$info=split(':',$msg);
	$add_to_list="<script language=\"javascript\">add_opt(\"".$info[0]."\",\"".$info[0]."\");</script>";
	$info_board="<script language=\"javascript\">document.getElementById('info_board').innerHTML=\"".$info[0];
	$info_board.="-".$info[1]." vient de se connecter\";</script>";
  }
  if($this->write_type == 2){
	$full_msg="<script language=\"javascript\">document.getElementById('content').innerHTML+=\"".$msg."\"</script>";
  }
 
  if($this->write_type == 3){
	$full_msg=$msg;
  }
//On parcourt le tableau des connexions
  while ($value = current($this->clients)) {
	if(is_resource($value)){
	  if($this->write_type == 1){
		if($value != $this->client){
		  $full_msg=$add_to_list.$info_board;
		}
		else{
		  $full_msg=$info_board;
		}
	  }
	  $this->Logging('Ecriture de '.$msg.' to '.$value);
	  if((@socket_write($value,$full_msg,strlen($full_msg))===false)){
		$this->Logging ('déconnexion de '.key($this->clients).':'.$value);
		$disconnected[]=key($this->clients);
//Si l'écriture vers un client ne fonctionne pas, on en déduit qu'il est déconnecté
		unset($this->clients[key($this->clients)]); 
	  }
	}
	next($this->clients);
  }
//Si des clients se sont déconnectés, on fait un appel récursif de cette fonction
//pour mettre à jour la liste des connectés et envoyer un message d'info
  if(count($disconnected)>0){ 
	$msg="<script language=\"javascript\">document.getElementById('info_board').innerHTML=\"";
	for($i=0;$i<count($disconnected);$i++){
	  $msg.=$disconnected[$i]." s'est déconnecté<br>";
	  $msg1.="<script language=\"javascript\">remove_opt('".$disconnected[$i]."');";
	}
	$msg.="\";</script>";
	$msg1.="</script>";
	$fullmsg=$msg.$msg1;
	$this->write_type=3;
	$this->Write_To_Clients($fullmsg); 
  }
  else{
	return;
  }
 
}
 
//Cette méthode rajoute des br pour limiter le nombre de caractères par ligne.
function Wrap_Message(&$msg){
  $j=0;
  for($i=0;$i<strlen($msg);$i++){
	$msg_wrapped.=$msg[$i];
	if($j == 50){
	  $msg_wrapped.='<br>';
	  $j=0;
	}
	$j++;
  }
  $msg=$msg_wrapped;
}
 
//Cette méthode envoie à tous les clients ceux qui sont connectés
function write_Connected(){
  reset($this->clients);
  while ($value = current($this->clients)) {
	if(is_resource($value)){
	  $msg.="<script language=javascript>add_opt(\"".key($this->clients)."\",\"".key($this->clients)."\")</script>";
	}
	next($this->clients);
  }
  $full_msg=$this->html.$msg;
  @socket_write($this->client,$full_msg,strlen($full_msg));
}
//Cette méthode donne des infos sur le processing du serveur
//On peut soit, stocker l'info dans un fichier log, soit afficher l'output
//sur la sortie standard.
function Logging($msg){
  if($this->log == 1){
	if(empty($this->fp_log)){
	  $this->fp_log=fopen($this->logfile,'w') or die($this->destroy('Erreur de création du fichier log'));
	}
	fwrite($this->fp_log,$msg."\n");
  }
  else{
	echo "\n".$msg."\n";
  }
  return;
}
//Cette méthode est appelée lorsque l'on stoppe le serveur
//et le stoppe de manière propre en fermant toutes les connexions
//clients et en leur envoyant un message au préalable.
function destroy($err){
  if($err != null){
	$this->Logging($err);
  }
  else{
	$this->Logging(socket_strerror(socket_last_error()));
  }
  reset($this->clients);
  while ($sock_cli = current($this->clients)) {
    @socket_close($sock_cli);
    next($this->clients);
  }
 
  if(is_resource($fp)){
	fclose($fp);
  }
  @socket_close($this->socket);
  die();
}
 
}
 
?>

6.5. Script du client principal qui sert à envoyer les messages au serveur

 
Sélectionnez

<html>
<head>
<style>
input{
      background-color:#E7F2F8;
}
</style>
</head>
<body background="images/background2.jpg">
<center><form name="msg" method="post">
<textarea style="background-color:#E7F2F8;color:#AF00AF;" rows="2" cols="50" name="message"></textarea>
<input type="submit" value="Envoyer">
</form></center>
</body>
</html>
 
<?
define('HEADER',5);
//Le fichier de config contient les variables principales telles que le port et l'adresse de l'hôte
require_once 'chat_config.php';
if(!empty($_POST['message'])){
//remplacement des caractères enter par des espaces
 $message=ereg_replace(chr(13).chr(10),' ',$_POST['message']);
//Creation de la socket
 $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die('Création de socket refusée');
//Connexion au serveur
 socket_connect($sock,$address,$port) or die('Connexion impossible');
//Codage de la longueur du Pseudo
 $header=sprintf('d',strlen($_GET['Pseudo']));
//Codage de la longueur du message
 $header.=sprintf('d',strlen($message));
//Construction du paquet à envoyer au serveur
 $paquet=$header.$_GET['Pseudo'].$message;
//Calcul de la longueur du paquet
 $write_len=strlen($paquet)+HEADER;
//Ecriture du paquet vers le serveur
 socket_write($sock,$paquet,$write_len);
//Fermeture de la connexion
 socket_close($sock);
}
?>

6.6. Les frames de l'interface client

 
Sélectionnez

<? require_once 'chat_config.php';
if(!isset($_GET['Pseudo'])){
  die('Pseudo obligatoire!');
}
if(strlen($_GET['Pseudo'])>15 || substr_count($_GET['Pseudo'],' ')>0){
  die('Le pseudo ne peut comporter d\'espace et doit faire au maximum 15 octets');
}
//on formatte le pseudo afin qu'il ait toujours une longueur de 15 octets
$pseudo=sprintf('% 15s',$_GET['Pseudo']);
?>
<frameset rows="80%,*" border="0">
//Ici, on dirige la 1ère frame vers l'adresse et le port créé par le serveur
<frame noresize src="http://<? echo $address.':'.$port;?>?Pseudo=<? echo urlencode($pseudo);?>">
<frame noresize src="send_message.php?Pseudo=<? echo $_GET['Pseudo'];?>">
</frameset>
 

6.7. Et enfin, le script permettant de démarrer le serveur

 
Sélectionnez

<?
require_once 'chat_config.php';
require_once 'chat.php';
//Normalement le chat est démarré via php-cli, donc cette directive n'est pas indispensable
//Cependant on peut le démarrer via le browser, et là, elle le devient
ini_set("max_execution_time",0);
//Instanciation de la classe chat_server
$chat = new Chat_Server();
//On assigne le html du fichier config à la propriété html de l'objet
$chat->html=$html;
$chat->denied=$chat->html.$denied;
$chat->Already_In_use=$chat->html.$already_in_use;
//On démarre le serveur de chat
$chat->Start($address,$port);
?>

6.8. Vérifier les connexions actives

Sous Linux, Windows et à peu près tous les systèmes Unix, la commande netstat permet de vérifier l'état des connexions TCP et UDP ouvertes. Les différents états de socket que vous pourrez rencontrer sont généralement:

  • LISTENING lorsque la socket est à l'écoute (socket_accept en PHP)
  • ESTABLISHED lorsqu'une communication est établie sur la socket
  • TIME_WAIT lorsqu'une socket a été clôturée et est en passe de l'être par l'OS
  • CLOSE_WAIT lorsqu'une socket n'est plus active mais n'a pas été clôturée explicitement

Il y a bien sûr moyen d'agir sur le comportement de TCP-IP et notamment de booster la fermeture des connexions qui est généralement de 2 minutes par défaut. Sous Linux, la commande usuelle est sysctl mais les variables peuvent aussi être modifiées via le répertoire virtuel /proc. La fonction socket_bind() ne fonctionne correctement que si le numéro de port ciblé n'est pas déjà utilisé par un autre processus. Grâce à netstat, il vous est possible d'identifier facilement si un port est déjà utilisé ou pas. Il faut aussi noter que sur la plupart des systèmes, les numéros de port inférieurs à 5000 sont souvent réservés à des applications telles que ftp,http,pop3,smtp...Sous Linux et Unix, ces services sont généralement visibles dans le fichier /etc/services. Donc, préférez utiliser des numéros de port supérieurs à 5000.

7. Avantages et inconvénients de créer un chat basé sur les sockets

Avantages:

  • Consomme peu de ressources
  • Très rapide
  • Ne nécessite pas de raffraîchissement de page
  • Ne nécessite pas de base de données

Désavantages:

  • Pas beaucoup de chance que ça fonctionne chez un hébergeur gratuit car ils refusent généralement la création de socket
  • Nécessite probablement une sécurité plus importante

8. Télécharger et utiliser les scripts

- Téléchargez les fichiers
- Un fichier "lisez_moi" est inclu dans l'archive. Lisez-le attentivement afin d'utiliser correctement le chat - Essayez ces scripts en local ou chez un hébergeur ayant activé les sockets (phpinfo --enable sockets)

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2004 Developpez. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.