Bonjour à tous !
Tout d’abord, oui j’ai déjà ouvert un topic sur cette histoire de chat multicast. Le problème est résolu et je viens pour une autre question, donc je me permet d’ouvrir un nouveau sujet, même si c’est le même projet à la base. Je n’ai pas non-plus amélioré mon code selon le post de Alexandre Laurent comme suggéré dans mon précédent sujet, car ça représente de nouvelles choses à apprendre, et pour l’instant je suis sur ce chat, je ne veux pas faire plusieurs choses en même temps.
Donc pour resituer, j’ai à réaliser le client et le serveur d’un chat multicast. J’ai mes 2 sous-projets qui compilent et qui s’exécutent sans que le compilateur ne râle. J’ai voulu essayer si tout fonctionnait. j’ai donc exécuté le client et le serveur en même temps sur le même ordinateur (le client se connecte à l’adresse 127.0.0.1) Et comme j’exécute depuis Qt Creator, je ne peux lancer qu’une seule instance du client. Si je teste dans ces conditions, ça fonctionne. J’envoie depuis le client, le serveur renvoie à tout le monde, donc au seul client connecté.
Je me suis quand même posé la question "que se passe-t-il si plusieurs clients envoient quelque chose en même temps ?" Comme j’ai un seul client d’actif, j’ai voulu simuler en faisant plusieurs envois depuis le serveur. Quand un client se connecte, le serveur envoie "un nouveau client vient de se connecter" à tout le monde. J’ai fait ma "simulation" en multipliant ce message. J’en envoie 3 en ajoutant 1, 2, et 3 à la fin. Le comportement que j’attend, c’est que le serveur envoie les 3 à la suite, donc dans le client avoir les 3 à la suite sans que l’utilisateur ne touche à rien.
Ce n’est pas ce qu’il se passe ! le client reçoit "un nouveau client vient de se connecter 1". Je tape "message 1" puis clique sur le bouton envoyer, et le client reçoit "un nouveau client vient de se connecter 2". Rebelotte 2 fois, et là je reçoit enfin "message 1" dans le client. "message 1" apparaît après avoir envoyé 3 autres messages. Je comprend que tous les envois du serveurs se mettent dans une file d’attente. Le serveur envoie un message, puis se met en attente. Et quand un nouveau message arrive, il est placé dans la file d’attente, puis le serveur regarde le premier qui vient dans la file d’attente, l’envoie à tout le monde, puis se stoppe. Je ne veux pas de se comportement, je veux que le serveur envoie tout ce qu’il y a à envoyer, puis qu’il se mette en attente. J’en déduit que je dois mettre un whie(…). Mais je ne sais pas que tester dans mon while, sachant que je n’ai pas explicitement programmé de file d’attente.
Je met le code de mon serveur ci-après pour pas casser ce que je dit.
Mes 3 annonces sont aux lignes 46, 47, et 48. Le slot qui est appelé quand un message est reçu par le serveur commence à la ligne 52("donneesRecues"), et la fonction qui permet d’envoyer à tout le monde commence à la ligne 78 ("envoyerATous").
Pouvez-vous m’aider à comprendre quelle condition tester dans mon while pour que les 3 annonces soient envoyées successivement, sans avoir besoin de recevoir un message à chaque fois ?
Merci par avance
fenetre_serveur.cpp
#include "fenetre_serveur.h"
#include <QString>
// Constructeur par défaut
fenetre_serveur::fenetre_serveur() : QWidget()
{
// Texte d'information du serveur
etatServeur = new QLabel(this);
// Parametres du bouton
boutonQuitter = new QPushButton("Quitter", this);
QObject::connect(boutonQuitter, SIGNAL(clicked()), qApp, SLOT(quit()));
// Disposition
layout = new QVBoxLayout(this); //Pour ne pas s'embêter avec les ->setGeometry
layout->addWidget(etatServeur);
layout->addWidget(boutonQuitter);
setWindowTitle("Fenetre du serveur");
// Demarrage du serveur
/* Songer a mettre ceci hors du constructeur plus tard : J'aimerai pouvoir faire plusieurs serveurs en simultanné */
serveur = new QTcpServer(this);
// Attente de la connexion de n'importe qui sur le port 50885.
if( !serveur->listen(QHostAddress::Any, 50885)){ /* Si possible laisser l'utilisateur choisir son port ou un port automatique*/
etatServeur->setText("Le serveur n'a pas pu demarrer : <br />" + serveur->errorString());
}
else{
etatServeur->setText("Le serveur a démarré sur le port <strong>" + QString::number(serveur->serverPort()) + "</strong>.<br /> Des clients peuvent maintenant se connecter");
QObject::connect(serveur, &QTcpServer::newConnection, this, &fenetre_serveur::nouvelleConnexion);
}
}
void fenetre_serveur::nouvelleConnexion()
{
// Nouveau client
QTcpSocket *nouveauClient = serveur->nextPendingConnection();
clients << nouveauClient;
// Signaux
QObject::connect(nouveauClient, &QTcpSocket::readyRead, this, &fenetre_serveur::donneesRecues); // signal readyRead introuvable dans la doc de QTcpSocket
QObject::connect(nouveauClient, &QTcpSocket::disconnected, this, &fenetre_serveur::deconnexionClient); //idem
// Annonces
fenetre_serveur::envoyerATous("Un nouveau client vient de se connecter 1"); // Annonce 1
fenetre_serveur::envoyerATous("Un nouveau client vient de se connecter 2"); // Annonce 2
fenetre_serveur::envoyerATous("Un nouveau client vient de se connecter 3"); // Annonce 3
}
void fenetre_serveur::donneesRecues()
{
// Recherche du QTcpSoccket du client qui envoi
//fenetre_serveur::envoyerATous("test de réponse\n");
QTcpSocket *socket = qobject_cast< QTcpSocket *>(sender());
if(socket == 0){ return; }
// Recuperation du message
QDataStream in(socket); /*rendre les if plus propres */
if(tailleMessage == 0){ // Si on a pas déjà la taille du message
if(socket->bytesAvailable() < (int)sizeof(quint16)){ // Si on a pas au moins un int, alors on a pas encore reçu le message en entier
return;
}
in >> tailleMessage; // Si on au moins un entier, alors on a la taille du message
}
if(socket->bytesAvailable() < tailleMessage) {return;} // Si on a pas encore le message entier, on attend
QString message;
in >> message; // On vide entièrement in dans message;
envoyerATous(message); // On l'envoi à tout le monde;
tailleMessage = 0; // On se rend prêt à recevoir un nouveau message;
}
void fenetre_serveur::envoyerATous(const QString& message)
{
QByteArray paquet; // Le paquet a envoyer est un vecteur d'octets
QDataStream out(&paquet, QIODevice::WriteOnly); // Simplement pour pouvoir ecrire dans le paquet avec <<. C'est du sucre syntaxique
out << (quint16) 0; // Réservation de l'emplacement pour la taille du message
out << message; // On place le message a proprement parler
/* Ajouter un traitement du message, pour mettre le pseudo de qui envoi, l'heure... */
out.device()->seek(0); // On se repalce au début du paquet
out << (quint16) (paquet.size() - sizeof(quint16)); // La taille du message compte pas dans la taille du message...
// Envoi
for(int i = 0; i < clients.size(); i++)
{
clients[i]->write(paquet); //On parcours les clients et on leur envoie un à un
}
}
void fenetre_serveur::deconnexionClient()
{
envoyerATous("Un client vient de se déconnecter");
// Qui ?
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
if( socket == 0) { return; }
clients.removeOne(socket); // On retire le client
socket->deleteLater();
/* Mettre des else !!! */
}
Je suis convaincu que le problème vient de mon fichier source. Je met quand même le header associé pour donner les déclarations.
fenetre_serveur.h
#ifndef FENETRE_SERVEUR_H
#define FENETRE_SERVEUR_H
// fichiers par défaut
#include <QWidget>
#include <QtNetwork>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>
class fenetre_serveur : public QWidget {
Q_OBJECT
public:
fenetre_serveur();
void envoyerATous(const QString& message); /*renommer en envoyer*/
private slots:
void nouvelleConnexion(); /*renommer en client_connexion*/
void donneesRecues(); /*renommer en recevoir*/
void deconnexionClient(); /*renommer en client_deconnexion*/
private:
// Elements de l'interface
QLabel *etatServeur;
QPushButton *boutonQuitter;
QVBoxLayout *layout;
// Elements de réseau
QTcpServer *serveur;
QList<QTcpSocket *> clients;
quint16 tailleMessage = 0;
};
#endif // FENETRE_SERVEUR_H