Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Site Web > PHP > Points particuliers > Comment utiliser les sockets avec php ? Comment faire un chat avec les sockets ? > Partie pratique : la création du chat > Le chat > Lecture du tutoriel

Le chat

Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Avatar
Auteur : azmeuk
Note : 16 / 20 (7 votes)
Visualisations : 8 525

Plus d'informations Plus d'informations
Maintenant qu'on sait bien manier les sockets et qu'on connaît un peu Ajax, on va pouvoir commencer à coder le chat.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Réflexions

Avant de se lancer tête baissée dans la création du chat, on va réfléchir un peu sur son architecture. Nous aurons bien sûr une partie serveur et une partie client. Intéressons-nous à la partie client en premier, parce qu'elle est, me semble-t-il, la plus simple.

Partie client


La partie client devra pouvoir envoyer et recevoir des messages : rien de plus logique. Nous séparerons donc tout d'abord (seulement au début, ne t'inquiète pas :D ), la partie client en deux : une page de réception, et une autre d'envoi. On aura, dans la partie réception, une boucle infinie, qui sera chargée de récupérer les messages et de les lire, puis, sur la page d'envoi, un script pour envoyer un message au serveur. Rien de bien compliqué, quoi. Nous appellerons notre page de réception reception.php, et notre page d'envoi send.php.

Partie serveur


La partie serveur est un peu plus complexe que la partie client : en effet, il va falloir que le serveur crée un port et l'écoute, accepte toutes les connexions entrantes, et comme il y aura plusieurs clients, il va falloir stocker toutes les sockets dans un tableau (une case par socket / client). Et enfin, à chaque message reçu, on va le renvoyer à chaque client. Et pour faire dans l'original, on va appeler cette page serveur.php.

Le client est roi

Attaquons la partie client. Et pour commencer tranquillement, on va faire la page d'envoi.

send.php



Première chose à faire, définir les variables de port et d'adresse :
Code : PHP
1
2
$adresse='127.0.0.1';
$port=35353;

Si le serveur n'est pas sur la même machine que le client, la valeur de $adresse doit être l'ip de l'ordinateur qui fera office de serveur.

Pour le moment, on n'enverra qu'un seul message possible (pour le moment :p ). Donc, on prépare le pseudo et le message.
Code : PHP
1
2
$message = 'Hello world';
$pseudo = 'azmeuk' ;


Puis nous allons construire notre message, notre paquet, nous allons afficher en premier le pseudo, qui sera séparé du message par un simple espace.
Code : PHP
1
$paquet = $pseudo.' '.$message


On crée donc une socket, et on se connecte au serveur :
Code : PHP
1
2
3
4
//Creation de la socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//Connexion au serveur
socket_connect($socket,$adresse,$port);

On écrit dans la socket, puis on la ferme.
Code : PHP
1
2
3
4
//Ecriture du paquet vers le serveur
socket_write($socket,$paquet,strlen($paquet));
//Fermeture de la connexion
socket_close($socket);

Voilà : c'est fini! Bon, je sais, c'est un code un peu léger, mais tu pourras l'améliorer après (faire des messages d'erreur personnalisés, etc., etc.) ; mais pour le moment, on va supposer que l'on ne rencontrera pas d'erreur.

reception.php


Ici, on va donc faire la partie qui recevra les messages. Comme on l'a vu tout à l'heure, cette page contiendra une boucle infinie, on va la réaliser avec ceci :
Code : PHP
1
set_time_limit(0);


Puis, comme tout à l'heure :
Code : PHP
1
2
3
4
$adresse='127.0.0.1';
$port=35353;

$pseudo='azmeuk' ;


On crée ensuite une socket vierge, et on la connecte au serveur :
Code : PHP
1
2
3
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!@socket_connect($socket, $adresse, $port))
        echo "Le serveur n'est pas lancé";

Le @ devant une fonction signifie qu'en cas d'erreur, l'erreur ne sera pas affichée, et le script continuera tel quel.


Si tout va bien, on continue, et on informe le serveur que l'on se connecte :
Code : PHP
1
2
3
4
else
        {
        $message = $pseudo." /connect";
        socket_write($socket, $message , strlen($message));


Il faut maintenant lancer la boucle infinie :
Code : PHP
1
2
3
4
5
6
7
8
9
while(true)
                {
                if($input = @socket_read($socket, $port))
                        {
                        $pseudo = stripslashes(utf8_decode(substr($input , 0 , strpos($input , ' '))));
                        $message = stripslashes(utf8_decode(substr($input , strpos($input , ' ')+1 , strlen($input))));
                        echo '['.$pseudo.']: '.$message.'<br />';
                        flush();
                        }

utf8_decode() sert ici à corriger le problème de charset causé par l'utilisation d'Ajax.

La boucle est lancée : ici, s'il y a une réponse, elle sera du type : "pseudo message" . On sépare donc le message en deux par le premier espace, puis on l'affiche sous la forme : "[pseudo]: message".

Ici, la fonction flush() a un rôle très important : elle permet de vider les buffers du serveur. En clair : d'habitude, le serveur web ne transmet une page qu'une fois que la page a fini de charger, ce qui ici n'arrivera jamais. C'est-à-dire que, pour reprendre l'image de la lettre, si la page était une lettre, alors le serveur attend d'avoir fini de rédiger la lettre pour l'envoyer, au lieu d'envoyer une lettre à chaque fois qu'il a fini d'écrire une ligne. Eh bien la fonction flush() va faire ça, elle va lui demander d'envoyer la lettre telle quelle, le serveur va lui demander d'afficher la page, même si elle n'a pas fini de charger.


Si jamais socket_read() renvoie false, c'est que le serveur est éteint ; on casse alors la boucle, et enfin on ferme le while.
Code : PHP
1
2
3
4
5
6
else
                        {
                        echo "Le serveur a été éteint";
                        break;
                        }
                }

On ferme la socket, et le script est fini.
Code : PHP
1
2
socket_close($socket);
        }

Le serveur

Coté serveur... Commençons par permettre à la page d'être exécutée à l'infini.
Code : PHP
1
set_time_limit(0);

Nous allons créer une fonction qui va démarrer le serveur.
Code : PHP
1
2
3
4
5
//On crée notre classe
class Chat_Server
{       
var $socket=NULL;
var $client=NULL;

Ici, on met la variable $clients en globale, car c'est le tableau dans lequel seront "rangées" toutes les connexions client : elle devra donc être accessible par d'autres fonctions.
Démarrons le serveur :
Code : PHP
1
2
3
4
//Méthode qui démarre le serveur
function Start($adresse,$port)
        {
        echo"Serveur lance\n";

On définit le tableau qui contiendra toutes les sockets clients.
Code : PHP
1
$this->clients=array();

Puis ensuite, tu sais faire :p . On crée la socket, on la lie, et on la fait écouter un port.
Code : PHP
1
2
3
4
5
6
//Création de la socket
        $this->socket = socket_create(AF_INET, SOCK_STREAM, 0);
        //on lie la ressource sur laquelle le serveur va écouter
        @socket_bind($this->socket, $adresse, $port) or die("\nPort déja utilise");
        //On prépare l'écoute
        socket_listen($this->socket);

Maintenant, on crée la boucle infinie, et on attend les connexions.
Code : PHP
1
2
3
4
5
//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 client est établie
                $this->client = socket_accept($this->socket);



On va maintenant analyser tous les messages qui vont entrer. On décortique le message, comme tout à l'heure :
Code : PHP
1
2
3
4
//Cette méthode lit les données reçues par un client et les redistribue
                $reception = socket_read($this->client , 255);
                $pseudo = substr($input , 0 , strpos($input , ' '));
                $message = substr($input , strpos($input , ' ')+1 , strlen($input));


Si le message est "/connect", alors cela veut dire que le client se connecte ("/connect" est le message qu'envoie reception.php une fois lancé) ; on va donc stocker sa variable de connexion dans un tableau. Et sinon, cela veut dire que le client envoie un message : on enverra alors la socket à tous les clients.

Premier cas : connexion


Code : PHP
1
2
3
4
5
6
//Le message est "/connect", donc on stocke la socket dans le tableau
                if($message == "/connect")
                        {
                        $this->clients[$pseudo]=$this->client;
                        echo "$pseudo connected\n";
                        }

Second cas : c'est un message


Code : PHP
1
2
3
4
//C'est donc on message, ici on va envoyer le message vers chacun des clients
                else
                        {
                        echo "Pseudo: [".$pseudo."] Message recu: [".$message."] Message envoye a : ";

Ensuite, on passe au peigne fin le tableau clients : à chaque case du tableau, on envoie le message.
Code : PHP
1
2
3
//On passe chaque case du tableau = chaque client, et on lui envoie le message
                        foreach( $this->clients as $nom_case => $socket_en_cours)
                                {

Ici, on va tenter d'envoyer un message au client en cours ; si ça réussit, tout va bien, sinon, c'est qu'il est déconnecté.

Premier cas : ça ne marche pas


Code : PHP
1
2
3
4
5
6
7
//Si ça ne marche pas, c'est qu'il est déconnecté
                                if(@socket_write($socket_en_cours, $reception, strlen($reception)) === false)
                                        {
                                        //La socket est enlevée du tableau
                                        unset($this->clients[$nom_case]);
                                        echo "[$nom_case s'est deconnecte]";
                                        }


Second cas, tout va bien


Code : PHP
1
2
else
                                        echo "$nom_case ";

On ferme le foreach, la socket en cours, le else, la fonction et enfin la classe :
Code : PHP
1
2
3
4
5
6
7
8
//On ferme la socket qui vient de nous apporter un message
                        socket_close($this->client);
                        echo"\n";
                        flush();
                        }
                }
        }
}

Et enfin, on lance le chat.
Code : PHP
1
2
$chat = new Chat_Server();
$chat->Start('127.0.0.1',35353);

Voilà : nous avons codé la page serveur, et c'était le plus dur (mais tellement t'es fort, t'a trouvé ça vachement simple ;) ). Si tu veux que le serveur tourne même quand tu as fermé la page, alors essaye d'utiliser la fonction ignore_user_abort(). Enfin, tu peux lancer la page via un navigateur (alors mettez un flush() à la fin de la fonction read_write), ou alors lancer ça dans une commande :

Personnellement, je trouve ça plus pratique.

Utilisation du chat, et de Ajax

Testons donc tout ça. On commence par lancer le serveur (non, sans blague ?!!? o_O ). Puis on va lancer reception.php, et en parallèle, send.php. Si tout va bien, dès que send.php est lancé, reception.php affiche "Hello World", et on peut réactualiser send.php autant de fois qu'on veut : on verra autant de fois "Hello World". Bref, tout ça c'est très sympa, mais au final, ça ne fait qu'un message possible à envoyer.
Très bien, on va alors créer une page client.php avec un formulaire d'envoi pour choisir le message qu'on voudra envoyer. Et c'est à partir de ce moment qu'Ajax va servir ; mais tout d'abord, on va créer une page d'accueil, pour que le client choisisse un pseudo.

entree.php
Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Chat</title>
</head>
<body>
<div>
<h3>Choisis un pseudo</h3>
<form action="client.php" method="get">
<input type="text" name="pseudo" value="pseudo" />
<input type="submit" value="Entrer" />
</form>
</div>
</body></html>


Une fois cette page exécutée (que tu peux appeler index.php, vu que ce sera la page d'accueil), l'utilisateur est redirigé vers client.php, la page où il va pouvoir envoyer des messages.

Créons une page simple avec un champ de texte.
Code : HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>Chat</title>
<body>
<div>
<input type="text" size="50" name="envoyer" id="envoyer" />
<input type="submit" value="Envoyer" onClick="envoi_message()" />
<input type="hidden" id="pseudo" value="<?php echo $_GET['pseudo']; ?>" />
</div>
</body></html>


Bon, c'est super, mais qu'est-ce qui se passe ici quand on tente d'envoyer le message ?

Eh bien la fonction javascript envoi_message() est exécutée, on va donc -grâce à Ajax- envoyer le message à la page send.php.
Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function envoi_message()
        {
        var xhr_object = null;

        if(window.XMLHttpRequest) // Firefox
           xhr_object = new XMLHttpRequest();
        else
                {
                if(window.ActiveXObject) // Internet Explorer
                        xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
                else // XMLHttpRequest non supporté par le navigateur
                        {
                        alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
                        return;
                        }
                }
       
        var method   = "POST";
        var filename = "send.php";
        var requete  = "message=" + document.getElementById("envoyer").value + "&pseudo=" + document.getElementById("pseudo").value;

        xhr_object.open(method, filename, true);

        xhr_object.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

        xhr_object.send(requete);

        //On efface le champ d'envoi, et on lui redonne le focus
        document.getElementById("envoyer").value="";
        document.getElementById("envoyer").focus();
        }

Si tu as bien lu et compris la partie théorique d'Ajax, il ne devrait alors y avoir ici aucun problème : on envoie des informations à send.php (qu'il recevra dans les variables $_POST['message'] et $_POST['pseudo'] ). Arf, mais j'allais oublier : il faut donc modifier la page send.php , pour qu'elle envoie le message choisi par l'utilisateur, et non plus "Hello world". Soit ! Rien de compliqué non plus ici :
Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php   
$address='127.0.0.1';
$port=35353;
if(!empty($_POST['message']))
        {
        //Création de la socket
        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        //Connexion au serveur
        socket_connect($sock,$address,$port);
        //Construction du paquet à envoyer au serveur
        $paquet=str_replace(' ' , '&nbsp;' ,$_POST['pseudo']).' '.$_POST['message'];
        //Ecriture du paquet vers le serveur
        socket_write($sock,$paquet,strlen($paquet));
        //Fermeture de la connexion
        socket_close($sock);
        }
?>


Refais les tests de tout à l'heure : serveur lancé, page reception.php aussi, et enfin, envoie tous les messages que tu veux avec client.php. Mais il se pose un problème, on a deux pages, et ce n'est pas très pratique, que faire ? Mettre des iframes trop-moches-pas-belles ? Non : on a la puissance d'Ajax de notre coté, exploitons-la : on va donc récupérer toutes les données affichées par reception.php, sur client.php -toujours grâce à Ajax- et on va la placer dans un div, on ajoute donc ça dans la page :
Code : HTML
1
<div id="content" style=" background-color:#E7F2F8; height:350; overflow:auto; "></div>

et puis on ajoute une petite fonction javascript :
Code : JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function reception()
        {
        var xhr_object = null;
        var div = document.getElementById('content');

        if(window.XMLHttpRequest) // Firefox
           xhr_object = new XMLHttpRequest();
        else
                {
                if(window.ActiveXObject) // Internet Explorer
                        xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
                else // XMLHttpRequest non supporté par le navigateur
                        {
                        alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
                        return;
                        }
                }
       
        var method   = "POST";
        var filename = "reception.php";
        var requete  = "pseudo=" + document.getElementById("pseudo").value;

        xhr_object.onreadystatechange = function()
                {
                if(xhr_object.readyState == 3)
                        {
                        var reponse = xhr_object.responseText;
                        document.getElementById("content").innerHTML = reponse;
                        div.scrollTop = div.scrollHeight;
                        }
                }

        xhr_object.open(method, filename, true);

        xhr_object.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

        xhr_object.send(requete);

        }

De même ici, c'est comme dans la partie théorique, mais une chose diffère : xhr_object.readyState == 3. Dans la partie théorique, la valeur était 4, c'est-à-dire que dès que la page réception avait fini de charger, on commençait à lire les données ; or ici, reception.php ne s'arrête que si le client s'en va ou que le serveur s'arrête, donc on met 3, ce qui signifie que les données sont lues dès qu'elles sont reçues.
Et puis il faut bien que la fonction reception() se lance dès le chargement de la page : pour ce faire, modifions la balise <body> pour donner ceci : "<body onLoad="reception()">"
Eh bien ça y est : c'est fini, notre chat est prêt. Fais des tests avec plusieurs personnes, ça marche ! Récapitulation de l'intégralité du code au chapitre suivant.

Chapitre précédent Sommaire Chapitre suivant
Retour en haut Retour en haut


Créé : le 31/01/2006 à 16:35:58
Modifié : le 25/09/2008 à 18:58:54
Avancement : 100%
Licence : Copie non autorisée

13 commentaires

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 83 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0212s (0.0105s)