Page 1 sur 2

[Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : dim. 05/janv./2014 20:40
par Yrahen
Bonjour à tous,

Voila quelques années que je parcours le forum tel un fantome dans un grenier, sans vraiment y contribuer.
Mais nous avons changé d'année, pour le meilleur et pour le pire, aussi j'ai décidé de rediger un mini tuto sur une facon de creer un tchat, et sans doutes beaucoup plus.

Tout d'abord, il faut savoir que quand je dis "sans serveur, sans BDD", je parle des serveurs virtuels ou autres. Il faut tout de meme un hebergeur pour.. du PHP. Et oui, le php va faire office de serveur : il gerera l'enregistrement des messages et leur affichage.

Je vous fourni ici les codes :

Code : Tout sélectionner

Tchat.php :

<?php
if(!is_file('Tchat.txt'))
{
	$Tchat=array();
	file_put_contents('Tchat.txt', serialize($Tchat));
}
if(isset($_GET['Action']))
{
	if($_GET['Action']=='Post' AND isset($_GET['Pseudo']) AND isset($_GET['Message']))
	{
		$Tchat=unserialize(file_get_contents('Tchat.txt'));
		$Tchat[time()]=$_GET['Pseudo'].':'.$_GET['Message'];
		file_put_contents('Tchat.txt', serialize($Tchat));		
	}
	elseif($_GET['Action']=='Refresh')
	{
		$Tchat=unserialize(file_get_contents('Tchat.txt'));
		ksort($Tchat);
		foreach($Tchat as $Time => $Message)
		{
			echo $Message.'<br/>';
		}
	}
}
?>

Code : Tout sélectionner

Votre fichier .pb
DelayRefresh=3 ; frequence d'actualisation du tchat en secondes

Enumeration
  #Fenetre
  #Editeur
  #Message  
  #Pseudo  
  #Police
EndEnumeration

Procedure.s Url2Text2(Url.s, OpenType.b,ProxyAndPort.s)
    isLoop.b=1
    INET_RELOAD.l = $80000000
    hInet.l=0
    hURL.l=0
    Bytes.l=0
    Buffer.s=Space(2048)
    RES.s=""   
   
    hInet = InternetOpen_("", OpenType, ProxyAndPort, "", 0)
    hURL = InternetOpenUrl_(hInet, Url, #Null, 0, INET_RELOAD, 0)
   
    Repeat
        InternetReadFile_(hURL,@Buffer, Len(Buffer), @Bytes)
        If Bytes = 0
            isLoop=0
        Else
            RES = RES + Left(Buffer, Bytes)
        EndIf
    Until isLoop=0
   
    InternetCloseHandle_(hURL)
    InternetCloseHandle_(hInet)
    ProcedureReturn RES
EndProcedure

Procedure.s Get(Url.s)
    ProcedureReturn Url2Text2(Url,1,"")
  EndProcedure
  
Procedure Refresh(Delay)

  Repeat
    Try$=Get("http://URLDUSCRIPT.fr/Tchat.php?Action=Refresh")
    Affichage$=ReplaceString(Try$,"<br/>",Chr(10))
    SetGadgetText(#Editeur,Affichage$)
    Delay(Delay*1000)
  ForEver

EndProcedure

OpenWindow(#Fenetre, 0, 0, 600, 560,"Yrahen", #PB_Window_SystemMenu)
LoadFont(#Police, "Tahoma", 14) 
SetGadgetFont(#PB_Default, FontID(#Police)) 
EditorGadget(#Editeur, 10, 10, 580, 490,#PB_Editor_ReadOnly|#PB_Editor_WordWrap)
StringGadget(#Message, 190, 510, 400, 40, "")
StringGadget(#Pseudo, 10, 510, 170, 40, "")
AddKeyboardShortcut(#Fenetre, #PB_Shortcut_Return, 15)

CreateThread(@Refresh(), DelayRefresh)

   Repeat
    Event = WaitWindowEvent()

    If Event = #PB_Event_CloseWindow  
      End
    ElseIf Event=#PB_Event_Menu And EventMenu()=15
      Pseudo$=GetGadgetText(#Pseudo)      
      Message$=GetGadgetText(#Message)      
      try$=Get("http://URLDUSCRIPT.fr/Tchat.php?Action=Post&Pseudo="+Pseudo$+"&Message="+Message$)
      SetGadgetText(#Message,"")
    EndIf

  ForEver
CE CODE EST UNE BASE, IL EST LOIIIIN D'ETRE OPTIMISé

Explication du code PHP :

En fait, quiconque à deja pratiqué le php dans sa vie se rendra vite compte que je n'ai utilisé que des bases. Pour les autres, en clair j'utilise un fichier Tchat.txt qui contient un array ( tableau ) contenant les pseudos/messages des tchateurs.
Pourquoi ne pas utiliser une base de données?
Pour plusieurs raisons. Une base de données c'est bien, mais c'est lent. De plus, le tchat est refresh regulierement, ce qui multiplie les ouvertures/fermetures/manipulations,ce qui peut amener un soucis d'overflow de la bdd. Et on a un gain de temps considerable avec le systeme de fichier.

Explication du code PureBasic :

Je tiens tout d'abord a grandement remercier celui qui a pondu la procedure Url2Text, elle est la base de ce code et de nombreux autres dont je me sert. Je ne me souviens pas ou je l'ai trouvé, mais je lui dit un grand merci et je lui offre le reste du champagne du nouvel an :) Pour ceux qui ne la connaissent pas, cette fonction permet de "lancer un script" sur un serveur distant. Ici on va activer le fichier Tchat.php.

Le code Pb est simple : un gadget Editeur dans lequel j'affiche les messages, 2 strings pour pseudo et messages.
Les messages sont affichés grace au code Php, dans la partie "Refresh", ou l'on affiche le contenu du tableau. Grace a Url2Text, on interroge le fichier Php qui renvoi les valeurs.
Les messages sont postés lorsque l'on appuie sur la touche ENTER : "If Event=#PB_Event_Menu And EventMenu()=15" ( je me suis tout betement suivi de la doc pour addkeyboardshorcut )
L'actualisation de l'affichage se fait grace a un thread, tout simplement. Dans ce thread, on interroge le serveur pour recuperer les messages et on les affiches dans l'editorgadget.


Tout cela n'est pas bien compliqué. Encore une fois, tout ceci n'est que base, rien de plus. Il est largement optimisable. Par exemple, a chaque refresh on recupere l'ensemble des messages alors que l'on pourrait recuperer seulement les nouveaux. C'est sans doutes simple grace a un systeme de timestamp. Par ailleurs, il n'y a pas d'auth, on peut changer de pseudo a notre guise, pourquoi ne pas faire un systeme de connexion par exemple.

J'ai voulu partager ce fragment de code parce que je le trouve extremement pratique, pour ma part je developpe un MUD ( sorte de mini mmorpg textuel un peu vintage ), basé sur Url2Text. Peut etre que ce code donnera a d'autres des idées funs, qui sait? :)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : dim. 05/janv./2014 22:29
par MetalOS
Merci je vais tester ;-)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : dim. 05/janv./2014 23:20
par falsam
je viens de tester. Je ne comprenais pas pourquoi je n'obtenais pas l'historique du chat mais après avoir tester avec deux users, j'ai compris que seul le dernier message de chaque user était affiché. Le principe est intéressant. Merci pour ce partage.

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 3:48
par Yrahen
J'ai posté par erreur le premier code, qui n'etait pas correct. J'ai pu les yeux en face des trous a cette heure ci, merci la télé et ses series qui rendent insomniaques.
Le probleme venait du fait que dans le array qui contient les messages, on stock par clé, clé qui est le pseudo. Hors si une personne poste plusieurs messages, le array va mettre a jour la clé existante, et donc remplacer l'ancien message par le nouveau.
Ma faute, c'est à jour :)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 10:09
par falsam
A mon avis le premier code php était mieux car il ne stockait pas l'intégralité du chat. Il n'est pas utile de retrouver l'intégralité du chat quand on exécute à nouveau le code pb.

L'historique du chat devrait être géré par pb par ajout des infos du fichier Tchat.txt dans une listicongadget par exemple de manière à avoir une lecture plus rapide du fichier Tchat.txt.

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 11:54
par Backup
Yrahen a écrit : Je tiens tout d'abord a grandement remercier celui qui a pondu la procedure Url2Text,
il s'agit de Pille (Forum anglais)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 14:51
par Yrahen
@Falsam : Il suffit du coup de modifier la partie 'Post' du fichier php. Pour le reste, chacun fait evoluer tout ca comme il le souhaite :)
@Dobro : Merci de l'info, je vais sans doutes aller lui faire un bisou!

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 16:12
par falsam
Yrahen a écrit :@Falsam : Il suffit du coup de modifier la partie 'Post' du fichier php. Pour le reste, chacun fait evoluer tout ca comme il le souhaite :)
C'est ce que j'ai fait en y ajoutant l'heure sur ton premier code php. Encore une fois, merci pour ce code :)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 17:03
par Ar-S
Merci pour ce partage que je testerai sans hésiter.
Pourquoi as tu privilégié le thread plutôt qu'un simple timer pour ton actualisation de l'affichage ?

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 18:23
par Yrahen
Pour etre honnete, tout simplement parce que le principe du thread est commun a une grande partie de langages, et que je ne suis pas du tout familier avec le systeme de timer.
Après je viens de regarder la doc, c'est vrai que du coup, ca a l'air assez simple a utiliser. Le seul truc qui m'embete, c'est que manifestement si un autre event est en cours, le timer "saute" du coup si t'envoi un message ( dans l'exemple du tchat ), ca reporte le refresh au prochain raffraichissement du timer si j'ai bien compris.

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : lun. 06/janv./2014 18:32
par Backup
Yrahen a écrit : ca reporte le refresh au prochain raffraichissement du timer si j'ai bien compris.
ben si tu met un refresh plus court (toute les Secondes) , aucuns problemes ! ;)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : mar. 07/janv./2014 8:22
par doudouvs
Yrahen a écrit : Pourquoi ne pas utiliser une base de données?
Pour plusieurs raisons. Une base de données c'est bien, mais c'est lent. De plus, le tchat est refresh regulierement, ce qui multiplie les ouvertures/fermetures/manipulations,ce qui peut amener un soucis d'overflow de la bdd. Et on a un gain de temps considerable avec le systeme de fichier.
Ha bon comment on fait avec une base de donnée de plusieurs millions de ligne, on attend 1/2 heure ?? php tu pense qu'il n'y à pas de limite. Prend comme exemple Wordpress : super un site (blog) vite fait, on rajoute plusieurs millier d'article 5 à 10 secondes voir plus pour afficher la home. En fait cela viens du fait que le développement est fait à l'arrache. je prend un site perso 6 000 000 ligne mysql taille de la base (1,3Gio), une page connecteur en php, résultat d'une recherche environ 0,012 secondes (temps de la requête + génération de la page php)

Si tu souhaite avoir un vrai Chat sans refresh = thread UDP socket (client to serveur to client) tu rajoute un ack de réception (client/serveur) sachant que l'UDP ne garantie pas l'envoi réception.

Autre solution socket TCP avec lui tu a un contrôle des flux par contre il garde la connexion ouverte ce qui limite.

Autrement merci pour ton partage :)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : mar. 07/janv./2014 10:32
par Backup
+1 .. je ne suis pas specialiste en la matiere, mais dire qu'une base de données c'est... lent !??
ça me parait enorme, car apres tout c'est je dirai le principale interet d'une base ... la rapidité d'accé aux infos qui la compose :)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : mar. 07/janv./2014 12:38
par Yrahen
Pour repondre sur le sujet des sockets, je sais que le principe existe en php, j'avais fait des bidules avec. Par contre en Pb je ne connais pas, mais ca peut m'interesser aussi ( par curiosité, histoire d'apprendre des nouveaux trucs )

Pour ce qui est de la "lenteur" d'une base comparée a un fichier, c'est effectivement plus lent. Le fait d'utiliser une BDD est cependant logique dans la plupart des cas. Pour reprendre Wordpress, on y stock des articles, avec des dates, differents auteurs eventuellement, des categories ect.. Dans ce genre de cas ( qui est la majorité des cas ), l'utilisation des fichiers serait totalement stupide vu qu'on a une quantite d'informations diverses importantes, et qu'une base de données permet de trier/selectionner/update sans difficultées les données.

Dans le cas du tchat basique que j'ai proposé, il n'y a qu'un couple Pseudo/Message. Si le tchat devait evoluer et avoir un systeme d'inscription avec enregistrement d'email/password, pourquoi pas un systeme de salons ect.. il faudrait evidement passer par une BDD. Cela permet un tri et un classement beaucoup plus aisé.

Ce que je veux dire, c'est qu'il serait dommage d'allourdir le code avec une base qui ne contiendrait qu'une seule table avec 2 champs.

Je ne fais absolument pas l'apologie des fichiers, loin de la. Mon propre site utilise une base de données via pdo, et j'en suis plus que satisfait. Mais dans le cas de 2 valeurs a enregistrer, ou l'on a pas besoin de les trier ni de faire des operations complexes dessus, l'utilisation de bdd est pour moi superflue.

Ca n'est que mon humble avis de programmeur du dimanche après midi ( le matin, c'est dodo ), et si l'un d'entre vous prefere les BDD, c'est un choix que je ne contesterais absolument pas :)

Re: [Tuto] Un Tchat sans serveurs, sans BDD, sans soucis

Publié : mar. 07/janv./2014 16:38
par doudouvs
Je parle pas des sockets php, je parle des sockets unix.

Pour les BD oui c'est plus lent qu'un fichier par contre dans un fichier on pourrait très bien avoir le même contenu avec les requêtes qui vont bien on arrive a faire exactement la même chose "trier/selectionner/update" bash,perl,php,c,c++ ect.. les un plus lent ou plus gourmands que les autres.

J'ai pris l'exemple de Wordpress uniquement pour dire que cela peut être très lent, ou très rapide avec un dev perso et une base de plusieurs million de ligne

si tu veux faire jouejoue avec les sockets voila un vieux code exemple :

Serveur

Code : Tout sélectionner

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>

char buffer[512];
int ma_socket;

void main ( void )
{
int client_socket;
struct sockaddr_in mon_address, client_address;
int mon_address_longueur, lg;

bzero(&mon_address,sizeof(mon_address));
mon_address.sin_port = htons(2000);
mon_address.sin_family = AF_INET;
mon_address.sin_addr.s_addr = htonl(INADDR_ANY);

/* creation de socket */
if ((ma_socket = socket(AF_INET,SOCK_STREAM,0))== -1)
{
  printf("la creation rate\n");
  exit(0);
}
signal(SIGINT,fin);
/* bind serveur - socket */
bind(ma_socket,(struct sockaddr *)&mon_address,sizeof(mon_address));

/* oreille socket */
listen(ma_socket,5);

/* accept connect */
mon_address_longueur = sizeof(client_address);
while(1)
{
  client_socket = accept(ma_socket,
                         (struct sockaddr *)&client_address,
                         &mon_address_longueur);

  if (fork() == 0)
  {
    close(ma_socket);

    lg = read(client_socket,buffer, 512);
    printf("le serveur a recu: %s\n",buffer);
    sprintf(buffer,"%s du serveur",buffer);
    write(client_socket,buffer, 512);
    shutdown(client_socket,2);
    close(client_socket);
    exit(0);
  }
}
shutdown(ma_socket,2);
close(ma_socket);
}
Client

Code : Tout sélectionner

#include <stdio.h>
#include <errno.h>
#include <signal.h>

#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define SERVEURNAME "*.*.*.*" // adresse IP du serveur

int to_server_socket = -1;

void main ( void )
{

char *server_name = SERVEURNAME;
struct sockaddr_in serverSockAddr;
struct hostent *serverHostEnt;
long hostAddr;
long status;
char buffer[512];

bzero(&serverSockAddr,sizeof(serverSockAddr));
hostAddr = inet_addr(SERVEURNAME);
if ( (long)hostAddr != (long)-1)
  bcopy(&hostAddr,&serverSockAddr.sin_addr,sizeof(hostAddr));
else
{
  serverHostEnt = gethostbyname(SERVEURNAME);
  if (serverHostEnt == NULL)
  {
    printf("gethost rate\n");
    exit(0);
  }
  bcopy(serverHostEnt->h_addr,&serverSockAddr.sin_addr,serverHostEnt->h_length);
}
serverSockAddr.sin_port = htons(30000);
serverSockAddr.sin_family = AF_INET;

/* create socket */
if ( (to_server_socket = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
  printf("create socket client loupee\n");
  exit(0);
}
/* req connect */
if(connect( to_server_socket,
            (struct sockaddr *)&serverSockAddr,
            sizeof(serverSockAddr)) < 0 )
{
  printf("Connection loupee\n");
  exit(0);
}
/* Send and read */
write(to_server_socket,"Holla Toto",4);
read(to_server_socket,buffer,512);
printf(buffer);
/* close connect */
shutdown(to_server_socket,2);
close(to_server_socket);
}