IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Problème de gestion d'évènement réseau


Sujet :

C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut Problème de gestion d'évènement réseau
    Bonjour,

    Je souhaite réaliser un programme de 'chat'. Mais après m'être renseigné sur plusieurs sites je n'ai pas réussi à en créer un.
    Mon problème est que j'aimerais pouvoir savoir quand un client essaie de se connecter au serveur, pour ensuite l'accepter ou alors savoir quand un message est envoyé au serveur pour pouvoir le renvoyer aux autres clients par exemple.
    J'ai pu voir sur internet qu'il y avait des évènements réseaux envoyer par Windows ou alors qu'il y a une solution utilisant des threads. Quelle est la meilleure solution ? Il y en a peut-être d'autre plus facile à utiliser ?
    En utilisant ‘winsock2.h’.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 371
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 371
    Points : 23 626
    Points
    23 626
    Par défaut
    Bonjour,

    C'est un sujet qui a été traité ici de très nombreuses fois. Fais une recherche sur les forums C et C++ concernant la fonction select(), notamment, voir aussi les cours et tutoriels pour apprendre C++.

    J'ai pu voir sur internet qu'il y avait des évènements réseaux envoyer par Windows ou alors qu'il y a une solution utilisant des threads. Quelle est la meilleure solution ? Il y en a peut-être d'autre plus facile à utiliser ?
    En utilisant ‘winsock2.h’.
    On suppose donc que tu travailles sous Windows. Si tu t'appuies directement sur ces primitives, ton programme ne sera pas portable.

    Ne t'orientes pas directement vers les threads. C'est la solution de facilité et c'est surtout une échappatoire pour contourner un problème théorique que tu n'as pas réussi à résoudre en amont. Et si cela peut-être élégant dans certaines situations où il y a peu de clients mais que les tâches à effectuer sont complexes, ça devient dramatique dans les situations simples où il y a un tout petit peu de montée en charge.

    Oublie complètement les threads pour le moment. Tu y reviendras quand tu maîtriseras parfaitement cette partie.

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut
    Oui, je ne l'ai pas précise, j'utilise Windows !

    J'ai donc utiliser la fonction select() pour mon serveur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    while(1)
    	{
    		for(int i = 0 ; i < 2; i++)
    		{
    			int ret = 0;
    			FD_ZERO(&readfs);
    			FD_SET(Client[0], &readfs);
    			FD_SET(Client[1], &readfs);
     
    			if((ret = select(Client[i] + 1, &readfs, NULL, NULL, NULL)) > 0)
    			{
    				if(FD_ISSET(Client[i], &readfs))
    				{
    					memset(buffer,0,sizeof(buffer));
    					recv(Client[i], buffer, sizeof(buffer),0);
    					cout<<buffer<<"Message recu";
    				}
    			}
    		}
    	}
    mais avec ce code, quand j'envoie un message depuis le client, le serveur m'affiche:
    Message recu
    , sans le buffer ?

    C'est donc qu'il a mal ou pas reçu le message, mais alors ou est l'erreur ?

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 371
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 371
    Points : 23 626
    Points
    23 626
    Par défaut
    Peut-on voir à quel endroit tu déclares « buffer » ?

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    fd_set readfs;
    char buffer[50];
    Placé juste avant le while(1)

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 371
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 371
    Points : 23 626
    Points
    23 626
    Par défaut
    J'avais mal lu ton code, qui est incorrect, désolé :

    select() est fait pour attendre simultanément qu'au moins un des sockets examinés se débloque. Tu écoutes donc simultanément les deux clients à la fois.

    Par contre, tu as placé ton select() à l'intérieur de ta boucle for, examinant ainsi d'abord le premier client puis le second.

    Donc, select() débloque parce que c'est le second client qui a envoyé quelque chose mais que tu fais un read() sur le premier client parce que c'est son tour, il est normal que tu ne reçoive rien.

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut
    Mais alors dans ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select(Client[i] + 1, &readfs, NULL, NULL, NULL))
    Que mettre à la place de Client[i] + 1 pour que le select() soit sur les 2 client a la fois ? Et ensuite comment savoir quel est le client qui a envoyé un message ?

  8. #8
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 371
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 371
    Points : 23 626
    Points
    23 626
    Par défaut
    select() va surveiller tous les descripteurs que tu as marqué avec FD_SET dans le readfs. Le premier paramètre sert simplement à indiquer à select() où s'arrêter. Oui, on aurait pu faire un vrai objet, mais l'appel système est très ancien. Il date d'une époque où la programmation orientée objet balbutiait et où on était très limité en mémoire.

    Après l'appel, tous les descripteurs sont remis à zéro et seuls ceux qui ont été débloqués (parfois plusieurs en même temps) sont marqués. Tu les vérifies avec FD_ISSET.

  9. #9
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut
    J'ai bien compris ton message, j'ai donc modifié mon serveur comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    	fd_set readfs;
    	char buffer[50];
     
    	while(1)
    	{
    		int ret = 0;
    		FD_ZERO(&readfs);
    		FD_SET(Client[0], &readfs);
    		FD_SET(Client[1], &readfs);
     
    		if((ret = select(Client[1] + 1, &readfs, NULL, NULL, NULL)) > 0)
    		{
    			if(FD_ISSET(Client[0], &readfs))
    			{
    				/* des données sont disponibles sur le socket */
    				/* traitement des données */
    				memset(buffer,0,sizeof(buffer));
    				recv(Client[0], buffer, sizeof(buffer),0);
    				cout<<buffer;
    			}
    			else if(FD_ISSET(Client[1], &readfs))
    			{
    				/* des données sont disponibles sur le socket */
    				/* traitement des données */
    				memset(buffer,0,sizeof(buffer));
    				recv(Client[0], buffer, sizeof(buffer),0);
    				cout<<buffer;
    			}
    		}
    	}
    Mais maintenant, pour le client, je dois faire en sorte qu'il puisse recevoir les messages du serveur. Le problème est que je programme a l'aide de Qt et que si je veux que Qt gère l'appui sur le bouton d'envoi et qu'en même temps il vérifie si la socket reçois un message, je ne peux pas faire une boucle infinie, ça bloquerais Qt ?!

    Merci pour tes précédentes réponses et pour les suivantes

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 371
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 371
    Points : 23 626
    Points
    23 626
    Par défaut
    Re,

    Citation Envoyé par michael093 Voir le message
    J'ai bien compris ton message, j'ai donc modifié mon serveur comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    	fd_set readfs;
    	char buffer[50];
    
    	while(1)
    	{
    		int ret = 0;
    		FD_ZERO(&readfs);
    		FD_SET(Client[0], &readfs);
    		FD_SET(Client[1], &readfs);
    
    		if((ret = select(Client[1] + 1, &readfs, NULL, NULL, NULL)) > 0)
    		{
    			if(FD_ISSET(Client[0], &readfs))
    			{
    				/* des données sont disponibles sur le socket */
    				/* traitement des données */
    				memset(buffer,0,sizeof(buffer));
    				recv(Client[0], buffer, sizeof(buffer),0);
    				cout<<buffer;
    			}
    			else if(FD_ISSET(Client[1], &readfs))
    			{
    				/* des données sont disponibles sur le socket */
    				/* traitement des données */
    				memset(buffer,0,sizeof(buffer));
    				recv(Client[0], buffer, sizeof(buffer),0);
    				cout<<buffer;
    			}
    		}
    	}
    Le « else » est en trop, ici : ton client « 1 » ne sera géré que si le client « 0 » n'a pas été signalé. S'ils se débloquent tous les deux en même temps, le second va être ignoré. Ensuite, tu as utilisé « Client[0] » dans le bloc du client 1.

    Il aurait été plus malin d'utiliser la boucle for() de tout-à-l'heure pour factoriser ce code, mais à l'intérieur du if(select()) et plus à l'extérieur. :-)

    Mais maintenant, pour le client, je dois faire en sorte qu'il puisse recevoir les messages du serveur. Le problème est que je programme a l'aide de Qt et que si je veux que Qt gère l'appui sur le bouton d'envoi et qu'en même temps il vérifie si la socket reçois un message, je ne peux pas faire une boucle infinie, ça bloquerais Qt ?!
    Effectivement, comme avec la plupart des autres frameworks, tu es obligé de passer la main à Qt pour le laisser gérer la boucle principale, que tu gères ici toi-même avec ton while(1). Et là, il n'y a pas trente-six façons de s'en sortir : soit tu utilises un thread pour séparer Qt de ton programme (ici ce serait justifié, et tu te retrouverais avec seulement deux threads au lien d'en avoir un par client), soit tu utilises les facilités réseau proposées par Qt pour les intégrer en son sein, et lui demander de continuer à gérer la boucle pour toi, en surveillant les sockets réseau en plus des événements de la GUI.

    http://doc.qt.nokia.com/4.7-snapshot...ogramming.html

  11. #11
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut
    Merci, j'ai corrigé mes erreurs bêtes et j'ai remis la boucle for().
    Je vais donc essayer maintenant de faire le client avec ce que propose Qt !

    Mais encore une question à propos du serveur. J'ai bêtement fait une boucle pour connecter mes deux clients dans le constructeur du serveur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for(int i = 0; i < 2; i++)
    		while((Client[i] = accept(server, (SOCKADDR *)&csin, &sinsize)) == INVALID_SOCKET);
    maintenant que je connais select(), j’imagine que le mieux serais de l'utiliser ( et non pas dans le constructeur mais dans la boucle infinie avec les autres select() ) mais comme tu dis
    select() va surveiller tous les descripteurs que tu as marqué avec FD_SET dans le readfs
    , alors quel socket surveiller ?(La socket server j'imagine) Et le descripteur va t'il être modifié quand une connexion est en attente ?

    ps: en rajoutant donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FD_SET(server, &readfs);

  12. #12
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 33
    Points : 7
    Points
    7
    Par défaut
    J'ai créé le client en utilisant Qt, mais maintenant les clients ne reçoivent que les 4 premières lettres de mon message alors que le serveur reçoit le message en entier ?!
    Le code de la fonction recevoir du client :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    	QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
    
    	QTextStream in(socket);
    
    	if(msgSize == 0)
    	{
    		if(socket->bytesAvailable() < (int)sizeof(quint16))
    			return;
    
    		in >> msgSize;
    	}
    
    	if (socket->bytesAvailable() < msgSize)
    		return;
    
    	// Si on arrive jusqu'à cette ligne, on peut récupérer le message entier
    	QString messageRecu;
    	in >> messageRecu;
    
    	// On affiche le message sur la zone de Chat
    	ui.textEdit->append(messageRecu);
    
    	// On remet la taille du message à 0 pour pouvoir recevoir de futurs messages
    	msgSize = 0;
    Dans ce cas j'utilise un QTextStream mais l'envoi de nombres ne fonctionne pas ? Si j'utilise un QDataStream, je ne reçois carrément rien !

Discussions similaires

  1. Problème de gestion des évènements
    Par drunkskater dans le forum Tkinter
    Réponses: 17
    Dernier message: 15/09/2012, 17h20
  2. [AC-2003] Problème de gestion des événements
    Par Shankara dans le forum IHM
    Réponses: 4
    Dernier message: 27/03/2012, 11h56
  3. Problème de gestion d'évènement
    Par Nwoaar dans le forum SDL
    Réponses: 4
    Dernier message: 08/05/2010, 00h40
  4. problème de gestion d'évènements
    Par gabest dans le forum C++/CLI
    Réponses: 1
    Dernier message: 13/10/2009, 12h15
  5. Problème de gestion d'évènement
    Par florent_de_brest dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 06/07/2006, 16h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo