par
MoutMout » 21 oct. 2015, 06:56
Bonjour à tous,
Alors voilà je suis sur un projet

, celui-ci contiendra un tchat sur la page d'accueil.
Aprés pas mal d'essaye j'ai trouvé que le meilleur compromis pour MOI était les sockets.
Le problème que je rencontre aujourd'hui est qu'à chaque fois que je vais sur une autre page que l'accueil et que je reviens sur l'accueil, l'historique du tchat est vierge...
Ducoup on ne peut pas voir les messages postés il y a à peine 30 secondes si on était pas sur la page d'accueil juste avant ...
Je demande de l'aide car je suis perdu et la documentation sur les sockets est très limite je trouve ...
Mon code est en deux parties, le serveur : websocket_serveur.php et le client : script.js
websocket_serveur.php
#!/usr/bin/php -q
<?php
/* Cette source est une reprise avec un profond remaniement du tutoriel de
http://sii-rennes.developpez.com/articles/un-chat-en-html5-avec-les-websockets/
qui n’est absolument pas fonctionnel en l’état actuel (<2015-06-28). C’est un bon départ
pour “tâter” voir ce que sont les websockets avec un serveur PHP. La source d’information
la plus sérieuse, mais qui n’est toujours pas “normative” est la fastidieuse mais abordable
http://tools.ietf.org/html/rfc6455 que je vous invite à parcourir (en anglais).
NOTE je suis convaincu que c’est au serveur (cette source) de traiter le cas de message de
plus de 125 caractères, ce qui n’est pas le cas (bon, pour un chat, tronquer coté
client).
TODO le document rfc6455 décrit de nombreux cas où la connexion doit être close (paquet
“non conforme”)
J’ai essayé avec Chrome et Firefox, Vous constaterez que Firefox est prompt au tennis de
table. Je crois que le sujet doit-être repensé et ré-écrit pour un chat plus sérieux. Il
y a des choses inutilisées ici, comme l’affectation d’un uniqid à un utilisateur. Tout se
confond: utilisateur, pseudo, socket, uniqid…
Ce script est "normalement" lancé depuis une console sur le cerveur. Il est possible de le
lancer depuis un fureteur, mais que si on peut rendre l'exécution "sans fin", ce que votre
hébergeur interdit sûrement.
Précision sur cette source:
- comme ce script est sensé être lancé depuis une console, j’utilise la couleur pour
“dynamiser” les messages de débogage, (c’est sympa et si vous regardez un peu, ça mange
pas beaucoup de pain !)
Pour la mise en forme:
- je “joue” des ressources de coloration de mon éditeur afin de “grouper” visuellement le
code
- Jamais de code sur la première colonne (sauf #) : strictement réservé au débogage
- Le “principal” du code en bas de source, les fonctions et autres définitions en haut, le
la plus “légère” à la plus “lourde”
- indentation de 1 espaces en JS, deux en PHP
- j’évite de dépasser 96 colonnes par ligne (fini l’écran 12 lignes x 40 colonnes)
*/
error_reporting(E_ALL);
define ("_cLiR_", "\x1B[0J");
define ("ConClr", "\x1Bc"); // Effacement de l'écran
define ("_N", "\x1B[1;30m"); // Noir
define ("_R", "\x1B[1;31m"); // Rouge
define ("_V", "\x1B[1;32m"); // Vert
define ("_J", "\x1B[1;33m"); // Jaune
define ("_B", "\x1B[1;34m"); // Bleu
define ("_M", "\x1B[1;35m"); // Magenta
define ("_C", "\x1B[1;36m"); // Cyan
define ("_G", "\x1B[1;37m"); // Gris (heu… blanc)
define ("C_", "\x1B[0;0m"); // fin de coloration
define("HOST", "IP.LOCAL.DU.SERVEUR"); // host
define("PORT", PORT-CHOISI); // port
function say ($msg="") { print($msg."\n"); } // Causerie "normale"
function cons ($l, $m="") { if (@$GLOBALS['debug']) print(_G.$l.": "._R.$m.C_."\n"); }
class User {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
public $id;
public $socket;
public $handshake;
public function __construct ($socket) {
$this->id=uniqid(); // Ici, le client est unique !
$this->socket=$socket; // La socket pour lui
$this->handshake=false; // Jamais "upgradée" (ou "handchecké")
}
}
function SocketCreate ($address, $port){
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_bind($master, $address, $port) or die("socket_bind() failed");
socket_listen($master, 1024) or die("socket_listen() failed");
print("Server Started : ".date('Y-m-d H:i:s')."\n");
print("Master socket : ".$master."\n");
print("Listening on : ".$address." port ".$port."\n\n");
return $master;
}
function ConnecteClient ($socket) { // Créer un client. "socket" est sa socket
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $sockStk, $usrStk;
$user=new User($socket); // $this-> :id, socket, handshake
array_push($usrStk,$user); // On le place dans la liste des clients
array_push($sockStk,$socket); // Et sa socket dans la liste des sockets
cons(__LINE__, $socket." CONNECTED!"); // Information de dégugging
cons(__LINE__, print_r($usrStk, !0).""); // Information de dégugging
}
function GetUserBySocket ($socket) {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $usrStk;
cons(__LINE__, _J.print_r($socket, !0).""); // Information de dégugging
$n=count($usrStk); // Nombre de connexion à cette socket
for($i=0; $i<$n; $i++) // Tous les utilisateur en revue
if($usrStk[$i]->socket==$socket) // Celui-ci:
return $usrStk[$i]; // Retourné le trouvé
return null; // NOTE Le cas d'une réponse "null" n'est pas traité
}
function DeconnecteClient ($socket) { // Supprimer utilisateur utilisant cette socket
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $sockStk, $usrStk;
$n=count($usrStk); // Nombre de connexion à cette socket
for($i=0; $i<$n; $i++) // Les utilisateur en revue
if ($usrStk[$i]->socket==$socket)
break; // $found <- $i : trouvé
$i<$n && array_splice($usrStk, $i, 1); // Trouvé : supprimé de la liste des utilisateurs
$index=array_search($socket, $sockStk); // Chercher la socket, maintenant
socket_close($socket); // Fermer la connexion
cons(__LINE__, $socket." DISCONNECTED!"); // Debugging
if($index>=0) array_splice($sockStk, $index, 1); // Supprimer de la liste si dedans
}
function Unmask ($text) { // "Dé-masquer" le "cadre" texte entrant
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
/* 1er octet: Drapeaux (ex: 0x81 -> Fin + Text
2nd octet: (maskFlag<<7) + len du texte avec cas spécial pour len>125 et len>65535
..
--- (3e, 5e ou 9e) masque 32 bits
ex:
89 [01..7D] mm mm mm mm tt......tt len caratères (texte[0] en frame[6])
Cas d'un texte "masqué" de lg < 126 car.
81 [01..7D] tt.......tt len caratères (texte[0] en frame[2])
Cas d'un texte de lg < 126 car.
89 7E ll ll mm mm mm mm tt......tt (texte[0] en frame[8])
Cas d'un texte "masqué" de lg > 126 et < 65535 car.
81 7E ll ll tt......tt (texte[0] en frame[4])
Cas d'un texte de lg > 126 et < 65535 car.
89 7F ll ll ll ll ll ll mm mm mm mm tt......tt (texte[0] en frame[12])
Cas d'un texte "masqué" de lg > 65535 et < 281474976710655 car.
81 7F ll ll ll ll ll ll tt......tt (texte[0] en frame[8])
Cas d'un texte de lg > 126 et > 65535 et < 281474976710655 car.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length | |
| cont., if payload len==127 | |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
NOTE Les trames TCP envoyées par les web-sockets (clients) sont toujours masquée) */
//cons(__LINE__, "masked: \""._C.bin2hex($text).C_."\".\n");
$length=ord($text[1])&0x7F; // Longeur du texte "utile"
$mFlgP=(ord($text[1])&0x80) ? 4 : 0; // Indicateur de masquage
$l=strlen($text); // Longueur de la trame TCP embarquée
if($length==0x7E) // Texte composé de moins de 65536 caractères
$pos=4; // Position du masque
elseif($length==0x7F) // Texte composé de plus de 65536 caractères
$pos=8; // Position du masque
else // Texte composé de moins de 126 caractères
$pos=2; // Position du masque
if ($mFlgP) // Le texte est "masqué" (0 ou 4)
for ($j=0, $i=$pos+4; $i<$l; ++$j, ++$i) // 4: lg. du masque
$text[$i]=$text[$i]^$text[$pos+($j&3)];
//cons(__LINE__, "Unmask: \""._J.bin2hex($text).C_."\".\n");
//cons(__LINE__, "messag: \""._R.substr($text, $pos+$mFlgP, $l-$pos-$mFlgP).C_."\",\n");
return(substr($text, $pos+$mFlgP, $l-$pos-$mFlgP));
}
function Mask ($text, $mask=0) { // Encode message for transfer to client. {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
// "$mask" est soit une chaîne de 4 caractères, soit 0
// NOTE Les messages envoyés par le serveur à une web-socket ne doivent pas être masqués
//cons(__LINE__, _G.$text.C_.", longueur: "._M.strlen($text).C_.".\n");
//cons(__LINE__, " "._M.bin2hex($text).C_.".\n");
$l=strlen($text); // Longueur du texte
if ($mask) // Réaliser le masquage
for ($i=0; $i<$l; $i++)
$text[$i]=$text[$i]^$mask[$i&3];
$mskB=$mask ? 0x80 : 0;
$entet=chr(0x80|($text=="PING" ? 0x9 : $text=="PONG" ? 0xA : 0x1)); // "Fin", "text"
if ($l<0x7E)
$entet.=chr($mskB|$l);
elseif ($l<0xFFFF)
$entet.=chr($mskB|0x7E).chr($l>>8).chr($l&0xFF);
else
$entet.=chr($mskB|0x7F).chr($l>>24).chr($l>>16).chr($l>>8).chr($l&0xFF);
$entet.=$mask ? $mask[0].$mask[1].$mask[2].$mask[3] : "";
//cons(__LINE__, _M.bin2hex($entet.$text).C_.".\n");
return $entet.$text;
}
function Handshaking ($user, $buffer) { // Utilisateur à "valider"
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
cons(__LINE__, "Requête \"Handshake\": reçu du client :"); // Debugging
cons(__LINE__, $buffer);
if(preg_match("/GET (.*) HTTP/" ,$buffer, $match)) $rsc=$match[1]; // ???
if(preg_match("/Host: (.*)\r\n/" ,$buffer, $match)) $hst=$match[1];
$headers=array();
$lines=preg_split("/\r\n/", $buffer);
foreach($lines as $line) {
$line=chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
$headers[$matches[1]]=$matches[2];
}
$secKey=$headers['Sec-WebSocket-Key'];
$secAccept=base64_encode(sha1($secKey.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
$upgrade ="HTTP/1.1 101 Web Socket Protocol Handshake\r\n". // hand shaking header
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Location: ws://$hst".$rsc."\r\n". // ???
"Sec-WebSocket-Accept: $secAccept\r\n\r\n";
socket_write($user->socket, $upgrade, strlen($upgrade));
$user->handshake=true; // Fait, ouffff!
cons(__LINE__, "Réponse :");
cons(__LINE__, $upgrade); // Debugging
cons(__LINE__, "Requête exécutée."); // Debugging
}
function Emet ($client, $msg){
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
say(__LINE__."> ".$msg);
$msg=Mask($msg); // NOTE inutile !
socket_write($client, $msg, strlen($msg));
}
function Process ($user, $msg) {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $sockStk, $usrStk;
// $action=Unmask($msg); // cons(__LINE__, bin2hex($msg));
$action=str_replace("\n", "\\n", Unmask($msg));
say(_G.__LINE__." ← ".C_.$action);
$arr=json_decode($action, !0);
cons(__LINE__, print_r($arr, !0));
$name=$arr['usr'];
cons(__LINE__, "\$name: \"$name\"");
if ($name) switch($name) {
case "system": say("L'hôte dit: "._R.$arr['mes']._C); break;
case "syscli": say("Un client dit: "._G.$arr['mes'].C_); break;
default : $action=Mask($action);
$n=count($usrStk); // Nombre de connexion à cette socket
for($i=0; $i<$n; $i++) // Tous les utilisateur en revue
socket_write($usrStk[$i]->socket, $action, strlen($action));
}
else switch ($action) {
case "PING":
$action=Mask("PONG");
socket_write($user->socket, $action, strlen($action));
break;
default : say(bin2hex($msg)." ????");
}
}
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
$debug=true;
cons(0, ConClr."\r");
$master=SocketCreate(HOST, PORT); // Création de la socket
$sockStk=array($master); // En en fait une pile
$usrStk=array(); // Liste des utilisateurs
while(true) {
$sSClone=$sockStk; // Cloner la référence : socket_select() modifie la pile
$write=$except=NULL; // Parce que &NULL n'est pas admis par socket_select()
socket_select($sSClone,$write,$except,NULL); // Voir si un message en attente -> $sSClone
foreach($sSClone as $socket) { // Observation des connectés
if($socket==$master) { // Tiens! La connexion originale !
//cons(__LINE__, _B."\$socket==\$master");
// NOTE Bloquera ici si aucune autre connexion n'existe
$client=socket_accept($master); // $client sera une nouvelle ressource "socket"
if($client<0) { // Une erreur est survenue
say(__LINE__.": socket_accept() failed"); // Oups !
continue;
}
else
ConnecteClient($client); // Connexion
}
else { // Une connexion déjà ouverte
//cons(__LINE__, _B."\$socket!=\$master");
$bytes=@socket_recv($socket, $buffer, 2048, 0); // Disposé à recevoir
if($bytes==0) // Si on n'a rien reçu maintenant :…
DeconnecteClient($socket); // …déconnexion de cette socket
else {
$user=GetUserBySocket($socket); // Retrouver l'utilisateur depuis sa socket
if(!$user->handshake) // Jamais valider…
Handshaking($user, $buffer);// …valider.
else
Process($user, $buffer); // Traiter le message reçu.
}
}
}
}
script.js
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
if(h < 10 )
{
var heure = '0'+h+':'+m;
}
else if(m < 10)
{
var heure = h+':0'+m;
}
else if(m < 10 && h < 10)
{
var heure = '0'+h+':0'+m;
}
else
{
var heure = h+':'+m;
}
const kStTxt=["Ouvrant","Ouvert","Fermant","Fermé"]
,kStCol=["yellow","green","orange","red"]
;
var ws
,colo="#969696"
,host="ws://IP.PUBLIQUE:PORT/"
;
function colz (m, c){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
return "<span style=color:"+c+">"+m+"</span>"
}
function linkU (u){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
return "<a id=connectusername href=accueil.php?page=profilamis&amis="+u+">"+u+"</a>"
}
function $ (id) { /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
return document.getElementById(id)
}
function usr (a) { /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
$("usr").innerHTML+=heure+" "+linkU(a.usr)+": "+colz(a.mes, a.col).replace(/\n/, "<br />")
element = document.getElementById('usr');
element.scrollTop = element.scrollHeight;
}
function log (m){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
// $("debug").innerHTML+="<br />"+m
}
function onkey(e){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
if(e.keyCode==13)
send();
}
function send(m){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
var _u, _t=$("message"), _p=$("pseudo");
if (_u=m===_u) {
m=_t.value;
sedo = _p.value;
if(!m) { alert("Il n'y a rien à envoyer !");return }
_t.value="";
_t.focus();
}
m="{\"usr\":\""+(_u ? sedo : "syscli")+"\""
+",\"mes\":\""+m+"\n\""
+",\"col\":\""+(_u ? colo : "red")+"\"}";
try{ ws.send(m); log('Sent: '+m); } catch(ex){ log(ex); }
}
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
function init () {
try{
ws=new WebSocket(host);
ws.onmessage=
function(m){
var _a;
eval("_a="+m.data);
usr(_a);
};
}
catch(ex){ log(ex); }
setInterval(function () { // "CONNECTING","OPEN","CLOSING","CLOSED"
}, 100)
}
Cordialement MoutMout
Bonjour à tous,
Alors voilà je suis sur un projet :-* , celui-ci contiendra un tchat sur la page d'accueil.
Aprés pas mal d'essaye j'ai trouvé que le meilleur compromis pour MOI était les sockets.
Le problème que je rencontre aujourd'hui est qu'à chaque fois que je vais sur une autre page que l'accueil et que je reviens sur l'accueil, l'historique du tchat est vierge...
Ducoup on ne peut pas voir les messages postés il y a à peine 30 secondes si on était pas sur la page d'accueil juste avant ...
Je demande de l'aide car je suis perdu et la documentation sur les sockets est très limite je trouve ...
Mon code est en deux parties, le serveur : websocket_serveur.php et le client : script.js
[b]websocket_serveur.php[/b]
[php]#!/usr/bin/php -q
<?php
/* Cette source est une reprise avec un profond remaniement du tutoriel de
http://sii-rennes.developpez.com/articles/un-chat-en-html5-avec-les-websockets/
qui n’est absolument pas fonctionnel en l’état actuel (<2015-06-28). C’est un bon départ
pour “tâter” voir ce que sont les websockets avec un serveur PHP. La source d’information
la plus sérieuse, mais qui n’est toujours pas “normative” est la fastidieuse mais abordable
http://tools.ietf.org/html/rfc6455 que je vous invite à parcourir (en anglais).
NOTE je suis convaincu que c’est au serveur (cette source) de traiter le cas de message de
plus de 125 caractères, ce qui n’est pas le cas (bon, pour un chat, tronquer coté
client).
TODO le document rfc6455 décrit de nombreux cas où la connexion doit être close (paquet
“non conforme”)
J’ai essayé avec Chrome et Firefox, Vous constaterez que Firefox est prompt au tennis de
table. Je crois que le sujet doit-être repensé et ré-écrit pour un chat plus sérieux. Il
y a des choses inutilisées ici, comme l’affectation d’un uniqid à un utilisateur. Tout se
confond: utilisateur, pseudo, socket, uniqid…
Ce script est "normalement" lancé depuis une console sur le cerveur. Il est possible de le
lancer depuis un fureteur, mais que si on peut rendre l'exécution "sans fin", ce que votre
hébergeur interdit sûrement.
Précision sur cette source:
- comme ce script est sensé être lancé depuis une console, j’utilise la couleur pour
“dynamiser” les messages de débogage, (c’est sympa et si vous regardez un peu, ça mange
pas beaucoup de pain !)
Pour la mise en forme:
- je “joue” des ressources de coloration de mon éditeur afin de “grouper” visuellement le
code
- Jamais de code sur la première colonne (sauf #) : strictement réservé au débogage
- Le “principal” du code en bas de source, les fonctions et autres définitions en haut, le
la plus “légère” à la plus “lourde”
- indentation de 1 espaces en JS, deux en PHP
- j’évite de dépasser 96 colonnes par ligne (fini l’écran 12 lignes x 40 colonnes)
*/
error_reporting(E_ALL);
define ("_cLiR_", "\x1B[0J");
define ("ConClr", "\x1Bc"); // Effacement de l'écran
define ("_N", "\x1B[1;30m"); // Noir
define ("_R", "\x1B[1;31m"); // Rouge
define ("_V", "\x1B[1;32m"); // Vert
define ("_J", "\x1B[1;33m"); // Jaune
define ("_B", "\x1B[1;34m"); // Bleu
define ("_M", "\x1B[1;35m"); // Magenta
define ("_C", "\x1B[1;36m"); // Cyan
define ("_G", "\x1B[1;37m"); // Gris (heu… blanc)
define ("C_", "\x1B[0;0m"); // fin de coloration
define("HOST", "IP.LOCAL.DU.SERVEUR"); // host
define("PORT", PORT-CHOISI); // port
function say ($msg="") { print($msg."\n"); } // Causerie "normale"
function cons ($l, $m="") { if (@$GLOBALS['debug']) print(_G.$l.": "._R.$m.C_."\n"); }
class User {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
public $id;
public $socket;
public $handshake;
public function __construct ($socket) {
$this->id=uniqid(); // Ici, le client est unique !
$this->socket=$socket; // La socket pour lui
$this->handshake=false; // Jamais "upgradée" (ou "handchecké")
}
}
function SocketCreate ($address, $port){
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_bind($master, $address, $port) or die("socket_bind() failed");
socket_listen($master, 1024) or die("socket_listen() failed");
print("Server Started : ".date('Y-m-d H:i:s')."\n");
print("Master socket : ".$master."\n");
print("Listening on : ".$address." port ".$port."\n\n");
return $master;
}
function ConnecteClient ($socket) { // Créer un client. "socket" est sa socket
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $sockStk, $usrStk;
$user=new User($socket); // $this-> :id, socket, handshake
array_push($usrStk,$user); // On le place dans la liste des clients
array_push($sockStk,$socket); // Et sa socket dans la liste des sockets
cons(__LINE__, $socket." CONNECTED!"); // Information de dégugging
cons(__LINE__, print_r($usrStk, !0).""); // Information de dégugging
}
function GetUserBySocket ($socket) {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $usrStk;
cons(__LINE__, _J.print_r($socket, !0).""); // Information de dégugging
$n=count($usrStk); // Nombre de connexion à cette socket
for($i=0; $i<$n; $i++) // Tous les utilisateur en revue
if($usrStk[$i]->socket==$socket) // Celui-ci:
return $usrStk[$i]; // Retourné le trouvé
return null; // NOTE Le cas d'une réponse "null" n'est pas traité
}
function DeconnecteClient ($socket) { // Supprimer utilisateur utilisant cette socket
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $sockStk, $usrStk;
$n=count($usrStk); // Nombre de connexion à cette socket
for($i=0; $i<$n; $i++) // Les utilisateur en revue
if ($usrStk[$i]->socket==$socket)
break; // $found <- $i : trouvé
$i<$n && array_splice($usrStk, $i, 1); // Trouvé : supprimé de la liste des utilisateurs
$index=array_search($socket, $sockStk); // Chercher la socket, maintenant
socket_close($socket); // Fermer la connexion
cons(__LINE__, $socket." DISCONNECTED!"); // Debugging
if($index>=0) array_splice($sockStk, $index, 1); // Supprimer de la liste si dedans
}
function Unmask ($text) { // "Dé-masquer" le "cadre" texte entrant
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
/* 1er octet: Drapeaux (ex: 0x81 -> Fin + Text
2nd octet: (maskFlag<<7) + len du texte avec cas spécial pour len>125 et len>65535
..
--- (3e, 5e ou 9e) masque 32 bits
ex:
89 [01..7D] mm mm mm mm tt......tt len caratères (texte[0] en frame[6])
Cas d'un texte "masqué" de lg < 126 car.
81 [01..7D] tt.......tt len caratères (texte[0] en frame[2])
Cas d'un texte de lg < 126 car.
89 7E ll ll mm mm mm mm tt......tt (texte[0] en frame[8])
Cas d'un texte "masqué" de lg > 126 et < 65535 car.
81 7E ll ll tt......tt (texte[0] en frame[4])
Cas d'un texte de lg > 126 et < 65535 car.
89 7F ll ll ll ll ll ll mm mm mm mm tt......tt (texte[0] en frame[12])
Cas d'un texte "masqué" de lg > 65535 et < 281474976710655 car.
81 7F ll ll ll ll ll ll tt......tt (texte[0] en frame[8])
Cas d'un texte de lg > 126 et > 65535 et < 281474976710655 car.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length | |
| cont., if payload len==127 | |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
NOTE Les trames TCP envoyées par les web-sockets (clients) sont toujours masquée) */
//cons(__LINE__, "masked: \""._C.bin2hex($text).C_."\".\n");
$length=ord($text[1])&0x7F; // Longeur du texte "utile"
$mFlgP=(ord($text[1])&0x80) ? 4 : 0; // Indicateur de masquage
$l=strlen($text); // Longueur de la trame TCP embarquée
if($length==0x7E) // Texte composé de moins de 65536 caractères
$pos=4; // Position du masque
elseif($length==0x7F) // Texte composé de plus de 65536 caractères
$pos=8; // Position du masque
else // Texte composé de moins de 126 caractères
$pos=2; // Position du masque
if ($mFlgP) // Le texte est "masqué" (0 ou 4)
for ($j=0, $i=$pos+4; $i<$l; ++$j, ++$i) // 4: lg. du masque
$text[$i]=$text[$i]^$text[$pos+($j&3)];
//cons(__LINE__, "Unmask: \""._J.bin2hex($text).C_."\".\n");
//cons(__LINE__, "messag: \""._R.substr($text, $pos+$mFlgP, $l-$pos-$mFlgP).C_."\",\n");
return(substr($text, $pos+$mFlgP, $l-$pos-$mFlgP));
}
function Mask ($text, $mask=0) { // Encode message for transfer to client. {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
// "$mask" est soit une chaîne de 4 caractères, soit 0
// NOTE Les messages envoyés par le serveur à une web-socket ne doivent pas être masqués
//cons(__LINE__, _G.$text.C_.", longueur: "._M.strlen($text).C_.".\n");
//cons(__LINE__, " "._M.bin2hex($text).C_.".\n");
$l=strlen($text); // Longueur du texte
if ($mask) // Réaliser le masquage
for ($i=0; $i<$l; $i++)
$text[$i]=$text[$i]^$mask[$i&3];
$mskB=$mask ? 0x80 : 0;
$entet=chr(0x80|($text=="PING" ? 0x9 : $text=="PONG" ? 0xA : 0x1)); // "Fin", "text"
if ($l<0x7E)
$entet.=chr($mskB|$l);
elseif ($l<0xFFFF)
$entet.=chr($mskB|0x7E).chr($l>>8).chr($l&0xFF);
else
$entet.=chr($mskB|0x7F).chr($l>>24).chr($l>>16).chr($l>>8).chr($l&0xFF);
$entet.=$mask ? $mask[0].$mask[1].$mask[2].$mask[3] : "";
//cons(__LINE__, _M.bin2hex($entet.$text).C_.".\n");
return $entet.$text;
}
function Handshaking ($user, $buffer) { // Utilisateur à "valider"
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
cons(__LINE__, "Requête \"Handshake\": reçu du client :"); // Debugging
cons(__LINE__, $buffer);
if(preg_match("/GET (.*) HTTP/" ,$buffer, $match)) $rsc=$match[1]; // ???
if(preg_match("/Host: (.*)\r\n/" ,$buffer, $match)) $hst=$match[1];
$headers=array();
$lines=preg_split("/\r\n/", $buffer);
foreach($lines as $line) {
$line=chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
$headers[$matches[1]]=$matches[2];
}
$secKey=$headers['Sec-WebSocket-Key'];
$secAccept=base64_encode(sha1($secKey.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
$upgrade ="HTTP/1.1 101 Web Socket Protocol Handshake\r\n". // hand shaking header
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Location: ws://$hst".$rsc."\r\n". // ???
"Sec-WebSocket-Accept: $secAccept\r\n\r\n";
socket_write($user->socket, $upgrade, strlen($upgrade));
$user->handshake=true; // Fait, ouffff!
cons(__LINE__, "Réponse :");
cons(__LINE__, $upgrade); // Debugging
cons(__LINE__, "Requête exécutée."); // Debugging
}
function Emet ($client, $msg){
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
say(__LINE__."> ".$msg);
$msg=Mask($msg); // NOTE inutile !
socket_write($client, $msg, strlen($msg));
}
function Process ($user, $msg) {
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
global $sockStk, $usrStk;
// $action=Unmask($msg); // cons(__LINE__, bin2hex($msg));
$action=str_replace("\n", "\\n", Unmask($msg));
say(_G.__LINE__." ← ".C_.$action);
$arr=json_decode($action, !0);
cons(__LINE__, print_r($arr, !0));
$name=$arr['usr'];
cons(__LINE__, "\$name: \"$name\"");
if ($name) switch($name) {
case "system": say("L'hôte dit: "._R.$arr['mes']._C); break;
case "syscli": say("Un client dit: "._G.$arr['mes'].C_); break;
default : $action=Mask($action);
$n=count($usrStk); // Nombre de connexion à cette socket
for($i=0; $i<$n; $i++) // Tous les utilisateur en revue
socket_write($usrStk[$i]->socket, $action, strlen($action));
}
else switch ($action) {
case "PING":
$action=Mask("PONG");
socket_write($user->socket, $action, strlen($action));
break;
default : say(bin2hex($msg)." ????");
}
}
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
$debug=true;
cons(0, ConClr."\r");
$master=SocketCreate(HOST, PORT); // Création de la socket
$sockStk=array($master); // En en fait une pile
$usrStk=array(); // Liste des utilisateurs
while(true) {
$sSClone=$sockStk; // Cloner la référence : socket_select() modifie la pile
$write=$except=NULL; // Parce que &NULL n'est pas admis par socket_select()
socket_select($sSClone,$write,$except,NULL); // Voir si un message en attente -> $sSClone
foreach($sSClone as $socket) { // Observation des connectés
if($socket==$master) { // Tiens! La connexion originale !
//cons(__LINE__, _B."\$socket==\$master");
// NOTE Bloquera ici si aucune autre connexion n'existe
$client=socket_accept($master); // $client sera une nouvelle ressource "socket"
if($client<0) { // Une erreur est survenue
say(__LINE__.": socket_accept() failed"); // Oups !
continue;
}
else
ConnecteClient($client); // Connexion
}
else { // Une connexion déjà ouverte
//cons(__LINE__, _B."\$socket!=\$master");
$bytes=@socket_recv($socket, $buffer, 2048, 0); // Disposé à recevoir
if($bytes==0) // Si on n'a rien reçu maintenant :…
DeconnecteClient($socket); // …déconnexion de cette socket
else {
$user=GetUserBySocket($socket); // Retrouver l'utilisateur depuis sa socket
if(!$user->handshake) // Jamais valider…
Handshaking($user, $buffer);// …valider.
else
Process($user, $buffer); // Traiter le message reçu.
}
}
}
}[/php]
[b] script.js[/b]
[php]var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
if(h < 10 )
{
var heure = '0'+h+':'+m;
}
else if(m < 10)
{
var heure = h+':0'+m;
}
else if(m < 10 && h < 10)
{
var heure = '0'+h+':0'+m;
}
else
{
var heure = h+':'+m;
}
const kStTxt=["Ouvrant","Ouvert","Fermant","Fermé"]
,kStCol=["yellow","green","orange","red"]
;
var ws
,colo="#969696"
,host="ws://IP.PUBLIQUE:PORT/"
;
function colz (m, c){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
return "<span style=color:"+c+">"+m+"</span>"
}
function linkU (u){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
return "<a id=connectusername href=accueil.php?page=profilamis&amis="+u+">"+u+"</a>"
}
function $ (id) { /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
return document.getElementById(id)
}
function usr (a) { /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
$("usr").innerHTML+=heure+" "+linkU(a.usr)+": "+colz(a.mes, a.col).replace(/\n/, "<br />")
element = document.getElementById('usr');
element.scrollTop = element.scrollHeight;
}
function log (m){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
// $("debug").innerHTML+="<br />"+m
}
function onkey(e){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
if(e.keyCode==13)
send();
}
function send(m){ /// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
var _u, _t=$("message"), _p=$("pseudo");
if (_u=m===_u) {
m=_t.value;
sedo = _p.value;
if(!m) { alert("Il n'y a rien à envoyer !");return }
_t.value="";
_t.focus();
}
m="{\"usr\":\""+(_u ? sedo : "syscli")+"\""
+",\"mes\":\""+m+"\n\""
+",\"col\":\""+(_u ? colo : "red")+"\"}";
try{ ws.send(m); log('Sent: '+m); } catch(ex){ log(ex); }
}
/// <s"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*">
function init () {
try{
ws=new WebSocket(host);
ws.onmessage=
function(m){
var _a;
eval("_a="+m.data);
usr(_a);
};
}
catch(ex){ log(ex); }
setInterval(function () { // "CONNECTING","OPEN","CLOSING","CLOSED"
}, 100)
}[/php]
Cordialement MoutMout