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

Informations pour bien débuter en PureBasic
Yrahen
Messages : 51
Inscription : sam. 01/sept./2012 4:14

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

Message 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? :)
Dernière modification par Yrahen le lun. 06/janv./2014 3:48, modifié 1 fois.
"L'imagination est plus importante que la connaissance."
Avatar de l’utilisateur
MetalOS
Messages : 1498
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

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

Message par MetalOS »

Merci je vais tester ;-)
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

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

Message 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.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Yrahen
Messages : 51
Inscription : sam. 01/sept./2012 4:14

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

Message 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 :)
"L'imagination est plus importante que la connaissance."
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

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

Message 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.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

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

Message 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)
Yrahen
Messages : 51
Inscription : sam. 01/sept./2012 4:14

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

Message 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!
"L'imagination est plus importante que la connaissance."
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

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

Message 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 :)
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Ar-S
Messages : 9539
Inscription : dim. 09/oct./2005 16:51
Contact :

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

Message 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 ?
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Yrahen
Messages : 51
Inscription : sam. 01/sept./2012 4:14

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

Message 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.
"L'imagination est plus importante que la connaissance."
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

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

Message 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 ! ;)
doudouvs
Messages : 244
Inscription : jeu. 07/mars/2013 19:02
Localisation : France Alsace / Espagne Girona

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

Message 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 :)
GCC 7.4.0 / PureBasic 5.71 / Ubuntu 18.04.3 LTS
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

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

Message 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 :)
Yrahen
Messages : 51
Inscription : sam. 01/sept./2012 4:14

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

Message 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 :)
"L'imagination est plus importante que la connaissance."
doudouvs
Messages : 244
Inscription : jeu. 07/mars/2013 19:02
Localisation : France Alsace / Espagne Girona

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

Message 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);
}
GCC 7.4.0 / PureBasic 5.71 / Ubuntu 18.04.3 LTS
Répondre