[C++] Problèmes sur un programme de chat

Problèmes sur un programme de chat [C++] - C++ - Programmation

Marsh Posté le 18-10-2001 à 21:15:48    

Salut à tous,  
 
Je code en ce moment un programme simple de chat (j'utilise les MFC, sous VC++ 6.0) et je rencontre actuellement un problème assez embêtant. J'utilise une classe dérivée de CAsyncSocket et j'exploite les messages OnConnect(), OnSend(), OnReceive(), etc... Le chat gère plusieurs clients, et il marche relativement bien... mis à part que lorsque je fais se suivre plusieurs instructions de type...
m_ClientSocket[i].Send("abcd", 4);
m_ClientSocket[i].Send("efgh", 4);
 
... eh bien il arrive que le récepteur ne reçoive qu'un seul message, et sous la forme "abcdefgh". Au lieu de recevoir une première fois "abcd", puis la seconde "efgh". Ca me fait penser à un problème de tampon mais je ne vois pas comment je peux résoudre ça.
 
Le plus étrange, et ce qui me fait penser à un bug, c'est que tout se passe bien quand je lance le projet en débug pas à pas, mais que les messages se concatènent automatiquement lorsque je le lance normalement. Ca ne le fait que dans certaines parties du programme, le plus souvent celles où les instructions Send() se suivent de près.
 
Ca me pose conrètement un problème lorsqu'un client se connecte au serveur et que ce dernier doit lui envoyer la liste des clients déjà connectés. Je suis forcé de faire suivre plusieurs fois Send(), et ça merde complètement (en fait je pourrais faire autrement, mais comme ça m'a fait le problème en plein test-chat avec un pote, je pense que ça peut arriver dans pas mal de cas).
 
Bref, si qqun a une idée, ça m'arrangerait bien...

Reply

Marsh Posté le 18-10-2001 à 21:15:48   

Reply

Marsh Posté le 18-10-2001 à 21:27:15    

c'est normal. oui, y'a du buffering, sûrement des deux côtés (client-serveur). en debug pas à pas, pas de buffering car les deux send() sont espacés dans le temps. enfin je subodore, car je ne connais pas le fonctionnement interne de la chose, mais ça doit pas être plus compliqué que ça.
 
j'ai pas bien compris ton pb d'envoi de listes ... t'as qu'à faire deux sockets, où est le pb ? c'est un peu comme le ftp, où il y a une socket pour envoyer les commandes (liste le répertoire, envoie moi tel fichier) et une autre pour récupérer les données.

Reply

Marsh Posté le 18-10-2001 à 21:27:30    

t'as essayé deja de faire mumuse avec SetSockOpt avec comme param SO_SNDBUF ?

Reply

Marsh Posté le 18-10-2001 à 21:45:53    

youdontcare a écrit a écrit :

c'est normal. oui, y'a du buffering, sûrement des deux côtés (client-serveur). en debug pas à pas, pas de buffering car les deux send() sont espacés dans le temps. enfin je subodore, car je ne connais pas le fonctionnement interne de la chose, mais ça doit pas être plus compliqué que ça.




 
Ah si y'en a pas du tout en mode Send(), je pense aussi que c'est ça. Je viens d'essayer de mettre des boucles vides du style :
for (k=0; k<20000000; k++)
     ;
 
... et ça marche, mais l'envoi de messages n'est plus du tout instantané, et puis ça crée d'autres problèmes... et pis c'est pas joli :D.
 

youdontcare a écrit a écrit :

j'ai pas bien compris ton pb d'envoi de listes ... t'as qu'à faire deux sockets, où est le pb ? c'est un peu comme le ftp, où il y a une socket pour envoyer les commandes (liste le répertoire, envoie moi tel fichier) et une autre pour récupérer les données.  




 
Eh bien en fait j'ai créé un tableau de CString (il s'appelle strPseudoList) qui contient le nom de chaque client connecté. Lorsqu'un nouveau client se connecte, je lui envoie la liste via une boucle for, entre k=0 et k=(le nombre de clients déjà connectés).  
 
Je suis newbie dans le domaine, ça ne m'étonnerait pas que je gère mal tout ça  ;)

Reply

Marsh Posté le 18-10-2001 à 21:46:49    

wpk a écrit a écrit :

t'as essayé deja de faire mumuse avec SetSockOpt avec comme param SO_SNDBUF ?  




 
Je vais essayer ça tout à l'heure et je te dis ce que ça donne ;)

Reply

Marsh Posté le 18-10-2001 à 23:21:55    

Bon bah je sais pas, ça merde encore. J'ai mis un SetSockOpt(), et y'a plus que le premier message qui s'envoie  :lol:  
 
En fait je connais pas du tout SocketOpt(), qqun sait ce que je dois mettre comme paramètres (int nOptionName, const void* lpOptionValue, int nOptionLen, int nLevel = SOL_SOCKET), mais surtout OU utiliser cette méthode, et pour quelles sockets ? (notamment du côté serveur ou je dispose à la fois de la socket serveur qui s'occupe d'accepter la connexion etc et d'une socket pour chaque client...).
 
Sinon, un Sleep(500) pourrait le faire ? Ca a l'air de marcher, mais j'ai peur que ça pose problèmes lorsque, par exemple, un client envoie un message au serveur en plein milieu de son dodo :D
 
HAILPEUU !   :pt1cable:

Reply

Marsh Posté le 19-10-2001 à 00:16:41    

si tu lis la msdn (la bible du programmeur zindoz), tu peux y trouver ceci:
 
The TCP_NODELAY option disables the Nagle algorithm. The Nagle algorithm is used to reduce the number of small packets sent by a host by buffering unacknowledged send data until a full-size packet can be sent. However, for some applications this algorithm can impede performance, and TCP_NODELAY can be used to turn it off. Application writers should not set TCP_NODELAY unless the impact of doing so is well-understood and desired, since setting TCP_NODELAY can have a significant negative impact on network performance. TCP_NODELAY is the only supported socket option which uses level IPPROTO_TCP; all other options use level SOL_SOCKET.
 
En gros, y'a un algo qui decide qd envoyer un paquet dependant à la fois du temps et de la taille des données à envoyer. Si par exemple tu veux envoyer des packets de 4 octets, tu appelle dans le constructeur (ou la ou tu fais l'initialisation de ta sock) SetSockOpt.
 
int bufferSize = 4;
asyncSock.SetSockOpt(SO_SNDBUF, &bufferSize, sizeof(int));
 
et voilu, tu envoye un packet des qu'il atteint 4 octets.  
 
Je pense qu'il y'a une autre solution aussi  
A supposer que ton buffer d'envoi soit declaré ainsi :
 
int bufferSize;
asyncSocket.GetSockOpt(SO_SNDBUF, &bufferSize, sizeof(int));
char *buf = new char[bufferSize];
 
memset (buf, 0, bufferSize);
strncpy(buf,"blabla",bufferSize);
asyncSocket.Send(buf, bufferSize);
 
ca devrait declencher un envoi immediat

Reply

Marsh Posté le 19-10-2001 à 00:57:43    

Hmm, je suis HS là, j'essaie ça demain. Ca m'a pas l'air bête de bidouiller de la sorte, même si je suis pas sûr à 100% d'avoir compris ton deuxième exemple (une explication rapide serait bienvenue pour que je m'en assure ;)).  
 
Je reverrai ça avec l'esprit plus clair demain. Sinon, que penser d'un Sleep() après chaque envoi ? Ca pourrait marcher ?  :??:

 

[edtdd]--Message édité par Sielfried--[/edtdd]

Reply

Marsh Posté le 19-10-2001 à 06:59:11    

d'accord avec wpk: ca send l'option TCP_NODELAY non utilisée à plein nez!!!
Par défaut, la pile TCP a des buffers d'une certaine taille. Quand tu commences à remplir ces buffers, l'envoi à traver le reseau est généré soit:
1. quand le buffer est plein
2. sur un timeout
 
Donc si tu fais:
m_ClientSocket[i].Send("abcd", 4);
Sleep(1000);
m_ClientSocket[i].Send("efgh", 4);
 
alors tu génrère bien l'envoi dans 2 buffers séparés car il y a eu un timeout entre les 2 appels de la fonction Send. (il me semble que le timeout est de l'odre de 300ms, mais rien de moins sur).
Par contre, si les 2 appels de la fonction se suivent immédiatement, alors tout est mis dans le même buffer.
 
L'option TCP_NODELAY force l'envoi du buffer à chaque fois, sans attendre qu'il soit plein.

Reply

Marsh Posté le 19-10-2001 à 11:40:51    

Bah écoutez je suis totalement de votre avis, mais quand j'active l'option dans le constructeur de la Socket, SetSockOpt() retourne 0, et quand je le fais après établissement de la connexion, ça retourne bien 1 mais ça ne change absolument rien à mon problème :(
 
Voici les deux lignes que je tape :
 
BOOL Option = true;
BOOL result = SetSockOpt(TCP_NODELAY, &Option, sizeof(BOOL), IPPROTO_TCP);
 
--
 
En gros du côté serveur, dans le message OnAccept(), juste après avoir accepté le client :
 
m_ServerSocket.Accept(m_ClientSocket[++i_ClientID]);
BOOL Option = true;
BOOL result = m_ClientSocket[i_ClientID].SetSockOpt(TCP_NODELAY, &Option, sizeof(BOOL), IPPROTO_TCP);
 
--
 
Et du côté client, dans le message correspondnant, OnConnect(), c'est-à-dire dès que la connexion est établie :
BOOL Option = true;
BOOL result = m_ClientSocket.SetSockOpt(TCP_NODELAY, &Option, sizeof(BOOL), IPPROTO_TCP);
 
--
 
Voilà, et ça ne change RIEN. J'ai pas encore trop essayé de voir avec SO_SNDBUF et SO_RCVBUF, les exemples de wpk avaient l'air intéressants, donc je vais essayer ça. Du genre, à chaque envoi, changer la taille du buffer d'envoi en exactement la taille de la donnée à envoyer.
 
En attendant peut-être que j'utilise mal TCP_NODELAY ?
 
Faudra aussi m'expliquer l'avantage du buffer ? Enfin je vois son intérêt, mais est-ce normal qu'il concatène les messages envoyés ? Parce que c'est bien ça qui pose problème, c'est pas le buffering en soi. Selon moi, le buffer devrait, après être rempli, renvoyer les messages un par un. Sinon il n'a pour seule utilité que de faire merder une telle application, et je pense pas que ce soit cohérent...  :(

Reply

Marsh Posté le 19-10-2001 à 11:40:51   

Reply

Marsh Posté le 19-10-2001 à 16:26:15    

:bounce:

Reply

Marsh Posté le 19-10-2001 à 16:42:26    

tu te prends la tête pour pas grand chose. tu n'as qu'à te faire un mini protocole pour envoyer ce que tu veux.
 
par ex, si tu veux envoyer à la fois les messages du chat et la liste des connectés, tu peux envoyer (par exemple) un truc ressemblant à de l'xml :
 
<message from="guest1046">bonjour, ça va ?</message>
<message from="guest7878">blah blah blah ... blah blah blah</message>
<clientList>guest1046, guest7878, admin</clientList>
<clientConnect>guest7845</clientConnect>
<message from="guest7845">blah....</message>
<clientDisconnect>guest1046</clientDisconnect>
 
tu parses le résultat, et hop ... par contre le sleep(xxx) c'est TRES PEU recommandable ... beuh ...

Reply

Marsh Posté le 19-10-2001 à 17:30:14    

Je suis pas un expert sur la question, mais voici ce que je crois savoir (va voir ici pour une source fiable).
 
Classiquement, TCP/IP attend une réponse (acknowledgement - ACK) pour chaque paquet envoyé avant d'en envoyer un autre.
Cette méthode est peu efficase et c'est pourquoi de nombreuses implémentations de TCP/IP (dont celle de Microsoft utilisée dans Windows) définissent une fenètre glissante (sliding window).
 
J'ai fait un petit schéma pour montrer le fonctionnement :
 
légende :
# : fenètre coulissante
o : données à envoyer
x : données confirmées comme étant reçues
 
####-------------------------  
ooooooooooooooooooooooooooooo
 
-####------------------------
xoooooooooooooooooooooooooooo
 
---####----------------------
xxxoooooooooooooooooooooooooo
 
Au fur et à mesure que les paquets sont confirmés (acknowledged), TCP/IP en renvoie d'autres. En fait, il est toujours en avance par rapport à la confirmation. Pour une meilleure explication (plus précise notamment), va voir ici.
 
La taille de la fenètre est de 8ko par défaut sous Windows. Ce qui veut dire que TCP/IP va envoyer tes deux paquets sans attendre de confirmation et que donc le récepteur va recevoir les deux paquets à se suivre. Donc quand ton récepteur va lire les données, il va lire les deux paquets à la fois.
 
Cette configuration est normale et je te déconseille fortement de la changer (tu risquerais de perdre énormément en débit). Une bonne explication est située ici (lis le passage "Packets are illusions" ). La méthode recommandée pour identifier deux messages est soit d'utiliser un préfixe de longueur, soit un délimiteur.
 
1. Préfixe de longueur:
 
void Send( char *Buffer, u_long BufferSize )
{
  send( mySocket, &htonl( BufferSize ), sizeof( u_long ), 0 );
  send( mySocket, Buffer, BufferSize, 0 );
}
 
De cette façon tous tes messages sont préfixés avec la taille du message et le recepteur lit cette taille avant de lire le message.
 
2. délimiteur:
 
const char Delimiter[] = "\r\n";
 
void Send( char *Buffer, u_long BufferSize )
{
  Buffer = (char *)realloc( (void *)Buffer, BufferSize + strlen( Delimiter ) );
  memcpy( (void *)(Buffer + BufferSize), Delimiter, strlen( Delimiter ) );
  send( mySocket, Buffer, BufferSize + strlen( Delimiter ), 0 );
}
 
Ici tous les messages se terminent par un délimiteur ("\r\n" ici) qui indiquera au récepteur que le message est fini. Le problème de cette méthode est que le délimiteur ne doit jamais être inclu dans le message.
 
Il est également possible de faire d'autre protocoles plus ou moins compliqués (voir l'exemple de youdontcare qui est une variante du délimiteur), mais l'idée est là.
 
Il ne faut pas s'appuyer sur la séparation par paquet de TCP/IP, mais définir soit même son propre protocole de transmission
 
disclaimer: Je ne suis pas un expert des sockets ou de winsock, ni de TCP/IP. J'ai pu faire des erreurs et je vous serais gré de me corriger (j'aime bien apprendre!), mais je pense que l'idée est là.


---------------
each day I don't die is cheating
Reply

Marsh Posté le 19-10-2001 à 18:34:50    

Bon, merci à vous, je crois que ça va bien m'aider. En fait je préfère me baser sur l'exemple de "youdontcare", avec des sortes de mots-clés placé entre <> (ça fait assez clair). Vu que j'utilise des CString, ça peut se faire assez facilement.  
 
Adettons que le serveur reçoive :
 
"<message>Client1: Bonjour</message><connect>Client2</connect>".  
 
Par une boucle je vérifie quel est le premier mot-clé de la chaîne reçue : ici <message>, ce qui veut dire que c'est un message envoyé par un client. Après quoi j'utilise les méthodes Right() puis Left() pour me procurer le message en lui-même, avant qu'il ne s'ajoute à la liste des messages. Je retire la partie entre <message> et </message> (ces derniers inclus) de la chaîne, et je relance un test pour savoir si un autre mot-clé est présent après. Ici, oui, "<connect>" est présent, je cherche la chaîne entre "<connect>" et "</connect>", qui est le nom du nouveau Client, que j'ajoute à la liste, etc etc... et cela jusqu'à ce qu'il n'y ait plus de mot-clé (en l'occurence plus rien) dans la chaîne.
 
Alors évidemment ça ferait tout merder si un client se mettait à écrire "</message> lalalal <connect>abc</connect>". Il faudrait donc, dans le cas où des mots-clés se trouvent dans le message envoyé par le client, faire en sorte de prévenir le serveur comme il se doit, par exemple par un autre mot-clé unique, plutôt long et qui serait le seul à ne pouvoir être contenu dans un message d'un client (du genre <keywords_in_message_warning></keywords_in_message_warning>.
 
Enfin voilà ce que je compte faire, ça me paraît assez simple et ça devrait fonctionner. Merci à vous !
 
Tiens petite question en passant : j'utilise un procédé un peu louche pour, à la réception d'un message par le serveur; identifier de quel client il s'agit.
 
Voilà ce que je met :
 
--
int i, nResult;
for (i=1; i<=i_ClientID; i++) //i_ClientID = numéro du dernier client
{
nResult = m_ClientSocket[i].Receive(pText, 1024);
if (SOCKET_ERROR != nResult)
break;
}
 
--
 
Et j'obtiens dans i le numéro du client qui a envoyé le message. Ca fait un peu bidouille, y'a sûrement un autre moyen non ?

 

[edtdd]--Message édité par Sielfried--[/edtdd]

Reply

Marsh Posté le 19-10-2001 à 18:47:37    

non. si tu ne connais rien à l'xml, va lire ça pour commencer : http://www.w3schools.com/xml/default.asp
 
l'xml, c'est un peu comme le html, avec qq libertés en plus et des restrictions. ce sont des balises hiérarchisées, qui peuvent avoir des attributs et un contenu.
 
<message from="guest" date="18 juillet 2000, 19h30">bonjour</message>
 
'from', 'date' sont des attributs, 'guest' et 'date' leurs valeurs.
'bonjour' est le contenu de la balise.
 
'guest', 'date', 'bonjour' ne peuvent pas contenir <, >. si tu as fait de l'html, c'est comme en html : on passe par les entités : &entity; -> &lt; = <  &gt; = > etc ...  
 
il faut donc reparser ta chaîne avant de la traiter. mais cela assure un document bien formé. la transformation est à faire dans les deux sens : chaîne chat -> chaîne xml pour l'envoi au serveur, chaîne xml -> chaîne chat pour l'affichage côté client.
 
//  
 
pour ton autre problème, j'en sais trop rien, je n'ai fait que des sockets asynchrones qui envoient des notifications de changement de leur état (je suis connectée, j'ai envoyé, j'ai lu, je suis prête à envoyer ...) tout en précisant quelle socket envoie la notif.

Reply

Marsh Posté le 19-10-2001 à 19:00:15    

Eh bien si je m'y connais en html mais pas en xml. Mais en quoi ai-je besoin de connaître ces notions ? C'est juste un exemple d'envoi avec des balises en tant que délimiteurs. Et en effet j'effectue les transformations à l'envoi et à la réception d'un message : tranformation de "Client: Salut !" en "<message>Client: Salut!</message>", puis le récepteur s'occupe de récupérer la chaîne contenue dans la balise et de l'afficher. Qu'est-ce qui ne va pas dans ma façon de faire ?  
 
Bon je suis plutôt newbie en C++ et carrément en MFC, et c'est le premier programme que je fais qui utilise des sockets, donc il se peut que je fasse pas encore les choses correctement. Mais du moment que la méthode fonctionne.
 
P.S. : que veux-tu dire par parser exactement ? En gros reconnaître chacun des messages séparément et en faire ce qu'il faut en fonction de leur nature, c'est ça ? Il me semble que c'est ce que je fais.

 

[edtdd]--Message édité par Sielfried--[/edtdd]

Reply

Marsh Posté le 19-10-2001 à 19:09:20    

y'a rien 'qui ne va pas', c'est juste bancal.  
 
transformer 'client: salut' en '<message>client: salut</message>' est ok, mais comme tu disais plus haut, un client qui tape '<message>bonjour' sera transformé en '<message><message>bonjour</message>', ce qui n'est pas de l'xml valide. il faut le transformer en ''<message>&lt;message&gt;bonjour</message>'
 
//
 
pour les sockets, tu peux regarder les exemples sur http://www.sockets.com/ par ex.
 
//
 
parser -> transformer une chaîne de caractère quelconques en une structure de données compréhensible. oui, c'est ce que tu fais.  
 
par ex les deux types de parsing de xml sont :
* créer un arbre des balises avec leurs contenus & attributs
* appeler une callback à chaque nouvelle balise en passant à la callback le nom de la balise, ses attributs et son contenu.
 
//
 
tu n'es pas obligé non plus de te coller à l'xml, tu peux envoyer des messages séparés par des retours à la ligne, avec en premier le type du message, puis ses données (un peu comme l'irc je crois).

Reply

Marsh Posté le 19-10-2001 à 20:21:42    

youdontcare a écrit a écrit :

y'a rien 'qui ne va pas', c'est juste bancal.  
 
transformer 'client: salut' en '<message>client: salut</message>' est ok, mais comme tu disais plus haut, un client qui tape '<message>bonjour' sera transformé en '<message><message>bonjour</message>', ce qui n'est pas de l'xml valide. il faut le transformer en ''<message><message>bonjour</message>'




 
Heu, à quoi correspondent "<" et ">" ? (oui je suis newbie, oui oui :D)
Bon, concernant ce problème, j'ai aussi la solution évoquée plus haut : je vérifie que le message du client ne contient aucun mot-clé tel que "<message>" avant de l'envoyer. Si il en contient, je l'encadre une nouvelle fois d'une balise <keywords_in_message_warning></keywords_in_message_warning>, qui représenteront les seules et uniques bouts de chaines que le client ne pourra entrer (et faudrait vraiment un coup de hasard fabuleux pour que ce soit le cas... au pire je rajoute un nombre énorme après le "warning" :D).
Cela dit je suis intéressé par ta solution, mais faut me dire à quoi correspondent exactement > et <  :??:  
 

Citation :

par ex les deux types de parsing de xml sont :
* créer un arbre des balises avec leurs contenus & attributs
* appeler une callback à chaque nouvelle balise en passant à la callback le nom de la balise, ses attributs et son contenu.


 
Hum, bon j'arrête pas d'entendre parler de callback, je n'ai vu que des exemples, alors si tu pouvais en passant me définir l'utilité d'une telle fonction, ça m'arrangerait  ;)  
 

Citation :

tu n'es pas obligé non plus de te coller à l'xml, tu peux envoyer des messages séparés par des retours à la ligne, avec en premier le type du message, puis ses données (un peu comme l'irc je crois). "


 
Et si le client tape "\r\n", comment le serveur va faire la différence entre un retour à la ligne et "\r\n" ???  
 
Enfin bon, là j'ai essayé le système des balises, ça marche très bien, je vois pas pourquoi je changerais :)
 
D'avance merci pour ces éventuelles précisions supplémentaires  :jap:

Reply

Marsh Posté le 19-10-2001 à 20:43:03    

tout d'abord : va lire le lien que j'ai filé plus haut.
 
* ta solution keywords_in_message marche. perso je trouve pas ça super élégant par contre :D suppose que le gars tape keywords_in_message ... ok, c'est complètement hypothétique, mais bon.  
 
&lt; = <
&gt; = >
 
c'est tout. c'est ce qu'on appelle des entités, tu peux trouver la liste des entités html ici : http://www.w3.org/TR/REC-html40/sgml/entities.html . lors du parsing du texte, ces entités sont remplacées par leur caractères correspondants. pourquoi l'utiliser dans ton cas ? c'est marqué au début de mon message précédent. tu convertis les < et les > à l'envoi, puis tu les reconvertis à la réception.
 
* callback = comme son nom l'indique ... sert à être notifié de qq chose dans un bout de code. c'est un pointeur sur fonction. par ex, si tu utilises le parser xml qui s'appelle EXPAT ( http://www.jclark.com/xml/expat.html , et qui au passage est vraiment _hyper_ simple à utiliser), comment peux-tu savoir ce qu'il est en train de parser ? par une callback. à chaque foir qu'expat parse une balise, il appelle ta callback avec le nom de la balise et ses attributs. pareil lorsqu'il finit de parser la balise.
 
* c'était au cas où tu faisais un truc similaire à l'ric, où quand le client tape entrée, il envoie le message.  
 
//
 
en annexe, un sample d'expat :  
* tu initialises le parser
* tu setupes tes callbacks avec le SetElementHandler() : tes callbacks sont en haut de la source : startElement() et endElement().
 
 
/* This is simple demonstration of how to use expat. This program
reads an XML document from standard input and writes a line with the
name of each element to standard output indenting child elements by
one tab stop more than their parent element. */
 
#include <stdio.h>
#include "xmlparse.h"
 
void startElement(void *userData, const char *name, const char **atts)
{
  int i;
  int *depthPtr = userData;
  for (i = 0; i < *depthPtr; i++)
    putchar('\t';);
  puts(name);
  *depthPtr += 1;
}
 
void endElement(void *userData, const char *name)
{
  int *depthPtr = userData;
  *depthPtr -= 1;
}
 
int main()
{
  char buf[BUFSIZ];
  XML_Parser parser = XML_ParserCreate(NULL);
  int done;
  int depth = 0;
  XML_SetUserData(parser, &depth);
  XML_SetElementHandler(parser, startElement, endElement);
  do {
    size_t len = fread(buf, 1, sizeof(buf), stdin);
    done = len < sizeof(buf);
    if (!XML_Parse(parser, buf, len, done)) {
      fprintf(stderr,
       "%s at line %d\n",
       XML_ErrorString(XML_GetErrorCode(parser)),
       XML_GetCurrentLineNumber(parser));
      return 1;
    }
  } while (!done);
  XML_ParserFree(parser);
  return 0;
}

Reply

Marsh Posté le 19-10-2001 à 20:54:47    

je n'ai peut être pas été bien clair avec la conversion 'à la réception' ...
 
1)ton client tape : bonjour, </message>, blah!
tu convertis les entités (si tu veux être compliant xml, ça voudra dire convertir <, >, &, ", ';)
2) le message converti :  
  bonjour, </message>, blah!
3) tu l'ensères dans les balises :  
  <message>bonjour, </message>, blah!</message>
4) tu l'envoies
5) le client le réceptionne
6) le client le parse : c'est là qu'en fait que tu vois que la conversion est utile : tu vas chercher la string '<message>' dans ce que tu viens de recevoir, puis '</message>' dans celle-ci pour extraire le message.
7) tu vas donc extraire :  
  bonjour, </message>, blah!
8) tu n'as plus qu'à convertir les entités en caractères:
  bonjour, </message>, blah!
9) et à afficher.

Reply

Marsh Posté le 19-10-2001 à 20:56:01    

putain pourquoi j'ai pas commencé par un simple étape par étape plutôt que de glander sur tous ces messages ... :) :D
 
//
 
c'est encore moi : à noter qu'expat est un parser 'au fur et à mesure', ce qui est parfait dans ton cas, car tu risques de recevoir les données n'importe comment : par ex,  
 
- <messa
- ge>bonjour,
- comment ça va ? </mess
- age>
 
il suffit de filer tout ça à expat, il va se débrouiller et appeler tes callback dès qu'il aura qq chose à traiter.

 

[edtdd]--Message édité par youdontcare--[/edtdd]

Reply

Marsh Posté le 19-10-2001 à 21:26:50    

youdontcare a écrit a écrit :

tout d'abord : va lire le lien que j'ai filé plus haut.
 
* ta solution keywords_in_message marche. perso je trouve pas ça super élégant par contre :D suppose que le gars tape keywords_in_message ... ok, c'est complètement hypothétique, mais bon.  




 
Oui, enfin h'aurais empêché d'envoyer le message dans ce cas, c'est toujours moins embêtant que d'engendrer une erreur :D.
 

Citation :

< = <
> = >
 
c'est tout. c'est ce qu'on appelle des entités, tu peux trouver la liste des entités html ici : http://www.w3.org/TR/REC-html40/sgml/entities.html . lors du parsing du texte, ces entités sont remplacées par leur caractères correspondants. pourquoi l'utiliser dans ton cas ? c'est marqué au début de mon message précédent. tu convertis les < et les > à l'envoi, puis tu les reconvertis à la réception.


 
Je vois, mais je suppose que dans mon cas (parsing "personnalisé" ), remplacer par ">" ou par n'importe quoi d'autre reviendrait au même à partir du moment ou ça parse comme il faut côté serveur. Me trompe-je ? :)
 
Hum par contre, ça merderait pas si le client balançait un message contenant à la fois (par exemple) "<message>" et ">" ? :D (putain, faudrait le faire là  :lol: ).
 

Citation :

* callback = comme son nom l'indique ... sert à être notifié de qq chose dans un bout de code. c'est un pointeur sur fonction. par ex, si tu utilises le parser xml qui s'appelle EXPAT ( http://www.jclark.com/xml/expat.html , et qui au passage est vraiment _hyper_ simple à utiliser), comment peux-tu savoir ce qu'il est en train de parser ? par une callback. à chaque foir qu'expat parse une balise, il appelle ta callback avec le nom de la balise et ses attributs. pareil lorsqu'il finit de parser la balise.


 
Hmm, oui ça a l'air utile. Je l'ai vu tellement de fois dans des exemples que j'imagine que ça s'utilise partout. Enfin il faudrait que moi-même ais à en utiliser pour comprendre l'utilité concrète, forcément. Ca ne devrait pas tarder.
 

Citation :

* c'était au cas où tu faisais un truc similaire à l'ric, où quand le client tape entrée, il envoie le message.


 
Euh je comprends pas là, ça change quoi le fait d'appuyer sur Entrée ?  :??:  
 
Sinon, merci pour ton explication/exemple sur l'utilisation d'Expat. Je pense pas l'utiliser pour le prog que je code actuellement, puisque je me débrouille parfaitement sans, mais la prochaine fois que j'utilise des sockets et que je gère plusieurs messages, j'irai voir du côté du lien que tu m'as donné, ça a l'air très utile et ça me fera sûrement gagner du temps ;)

Reply

Marsh Posté le 19-10-2001 à 21:31:54    

gatorette a écrit a écrit :

 
Il ne faut pas s'appuyer sur la séparation par paquet de TCP/IP  




 
Et oui, tu as fait une erreur. Il n'y a justement pas de notion de paquets avec TCP. Il s'agit juste d'un flux de donnees.
 
Par exemple, si tu envoie "abcd", puis "efg" puis "hijkl", tu sais que le client recevra ces 12 lettres, dans l'ordre, mais rien de plus. Tu perds les 'separations'.
UDP, c'est le contraire, ce sont des paquets qui circulent.
 
L'idee de prefixer chaque paquet de sa taille est tres bonne. Cela permet de savoir combien d'octets sont attendus et evite de rester en attente pour rien.
J'evite d'en dire plus, j'y connais rien a l'implantation des MFC. Mais si vous avez des questions sur l'utilisation des sockets au niveau de l'API, n'hesitez pas a me mailer.

Reply

Marsh Posté le 19-10-2001 à 21:37:22    

Eh bien, merci pour le pas à pas, j'en demandais pas tant ! Mais ça confirme ce que j'avais compris. Cela dit y'a encore le problème du client qui se met à écrire ">" en plein milieu de son message :D...
 
Quant à Expat avec mon programme, oui, si tu le dis, ça pourrait convenir. Enfin pour l'instant je reste comme ça, on change pas un code qui marche :D  
 
En effet, les messages se réceptionnent comme il faut apparemment, enfin faut dire que je teste mon chat en local, et que je ne peux pas encore me mettre dans la situation où un gros tas de données serait envoyé au même moment.

Reply

Marsh Posté le 19-10-2001 à 21:38:15    

Sielfried a écrit a écrit :

Hum par contre, ça merderait pas si le client balançait un message contenant à la fois (par exemple) "<message>" et ">" ? :D (putain, faudrait le faire là  :lol: ).


bon va lire de la doc sur le xml. un document xml qui se valide (qd tu le drag n drope sur ie6, il l'affiche sans broncher) garantit justement que ce genre de problèmes n'arrivera jamais. c'est pourquoi je parlais de <, >, &, ', " .... car '&' doit être remplacé par '&'. tout comme en html ...
 
pour le fait d'appuyer sur entrée, c'était rapport à ton message précédent : "Et si le client tape "\r\n", comment le serveur va faire la différence entre un retour à la ligne et "\r\n" ???  
" ... d'ailleurs taper \r\n sur irc ne fait rien du tout, \r\n occupe quatre caractères, \r\n en C est converti en deux caractères, 13 et 10.
 
bref ...

Reply

Marsh Posté le 19-10-2001 à 21:39:38    

Sielfried a écrit a écrit :

Eh bien, merci pour le pas à pas, j'en demandais pas tant ! Mais ça confirme ce que j'avais compris. Cela dit y'a encore le problème du client qui se met à écrire ">" en plein milieu de son message :D...
 
Quant à Expat avec mon programme, oui, si tu le dis, ça pourrait convenir. Enfin pour l'instant je reste comme ça, on change pas un code qui marche :D  
 
En effet, les messages se réceptionnent comme il faut apparemment, enfin faut dire que je teste mon chat en local, et que je ne peux pas encore me mettre dans la situation où un gros tas de données serait envoyé au même moment.  



cf mon message au-dessus. expat ... si ton code marche déjà, effectivement, ne change rien.

Reply

Marsh Posté le 19-10-2001 à 21:50:19    

youdontcare a écrit a écrit :

 bon va lire de la doc sur le xml. un document xml qui se valide (qd tu le drag n drope sur ie6, il l'affiche sans broncher) garantit justement que ce genre de problèmes n'arrivera jamais. c'est pourquoi je parlais de <, >, &, ', " .... car '&' doit être remplacé par '&'. tout comme en html ...




 
Ouais, je suis pas encore allé sur les liens sur XML que tu m'as filés. J'ai pas trop envie de m'embrouiller l'esprit cela dit, ce que je fais marche bien et j'ai l'impression que ma solution, bien qu'un peu moins "pro", pourra convenir parfaitement.
 

Citation :

pour le fait d'appuyer sur entrée, c'était rapport à ton message précédent : "Et si le client tape "\r\n", comment le serveur va faire la différence entre un retour à la ligne et "\r\n" ???  
" ... d'ailleurs taper \r\n sur irc ne fait rien du tout, \r\n occupe quatre caractères, \r\n en C est converti en deux caractères, 13 et 10.


 
Eh bien justement, le client (et le serveur, d'ailleurs, puisqu'il participe au chat) envoie son message en appuyant sur Entrée dans le cas de mon prog. Mais du côté serveur, pour chercher les délimiteurs, il faudrait chercher la chaîne "\r\n" non ? D'où la confusion.  
 
De toutes façons, je n'ai pas choisi cette méthode, et si je change, j'utiliserai directement Expat.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed