Mourad2009B
Messages postés118Date d'inscriptionlundi 23 août 2010StatutMembreDernière intervention 5 février 2025
-
Modifié le 22 sept. 2022 à 11:41
Mourad2009B
Messages postés118Date d'inscriptionlundi 23 août 2010StatutMembreDernière intervention 5 février 2025
-
22 sept. 2022 à 17:35
Bonjour à tous,
J’ai réalisé un petit programme Client-Serveur en Qt, mais en utilisant pthread pour permettre une indépendance du Framework Qt et surtout pour la portabilité.
Serveur
header.h : ce fichier contient les inclusions communes et quelques #define
#ifndef HEADER_H
#define HEADER_H
// On inclut les fichiers standards#include<iostream>#include<stdlib.h>#include<stdio.h>#include<pthread.h>#include<thread>#include<mutex>//Les Fichiers Qt#include<QPlainTextEdit>#include<QPushButton>#define PORT 10000#ifdefined(WIN32)//Si nous sommes sous WINDOWS #include<winsock2.h> #include<windows.h>//On peut remarquer que le type socklen_t qui existe sous Linux, n'est pas défini sous Windows. Ce type sert à stocker la taille d'une structures de type sockaddr_in. Ça n'est rien d'autre qu'un entier mais il nous évitera des problèmes éventuels de compilation sous Linux par la suite. Il va donc falloir le définir nous même à l'aide du mot clef typedef comme il suittypedefint socklen_t;#elifdefined(linux)//Si nous sommes sous LINUX #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<unistd.h>// Define, qui nous serviront par la suite #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #defineclosesocket(s)close(s)// De mêmetypedefint SOCKET;typedefstructsockaddr_in SOCKADDR_IN;typedefstructsockaddr SOCKADDR;#endifstructPersonnage{char nom[256];char prenom[256];int age;};typedefstructPersonnage Personnage;structShared{int data;pthread_mutex_tmut;pthread_cond_tsynchro;};structThreadData{QPlainTextEdit*strucPlainText;QPushButton*strucBtnOn;QPushButton*strucBtnOff;structShared*psh;};structMsgData{char nom[256];char message[256];};#endif// HEADER_H
classserver.h :
#ifndef CLASSSERVER_H
#define CLASSSERVER_H
#include"header.h"#include<QDebug>#include<QPlainTextEdit>#include<QString>classClassServer{private:unsigned temp =GetConsoleOutputCP();/* Socket et contexte d'adressage du serveur */staticSOCKETsock;staticSOCKADDR_INsin;staticsocklen_trecsize;/* Socket et contexte d'adressage du client */staticSOCKETcsock;staticSOCKADDR_INcsin;staticsocklen_tcrecsize;staticint sock_err;staticQPlainTextEdit*m_plainTextEdit ;staticstructThreadData m_threadData;staticbool m_graphic;staticstructMsgData m_msgDataServer;public:ClassServer();ClassServer(structThreadData&myThreadData);~ClassServer();staticboolInitialisation();staticvoid*FctListenClient(void*);staticvoidFctSendMessage(structMsgData&);staticboolFctDeconnect();};#endif// CLASSSERVER_H
classserver.cpp
#include"classserver.h"SOCKETClassServer::sock =0;SOCKADDR_INClassServer::sin;socklen_tClassServer::recsize =sizeof(sin);SOCKETClassServer::csock =0;SOCKADDR_INClassServer::csin =SOCKADDR_IN();socklen_tClassServer::crecsize =sizeof(csin);int ClassServer::sock_err =0;
QPlainTextEdit* ClassServer::m_plainTextEdit = nullptr;structThreadData ClassServer::m_threadData;bool ClassServer::m_graphic =false;structMsgData ClassServer::m_msgDataServer;//Constructeur par défaut
ClassServer::ClassServer(){
m_graphic =false;}//Constructeur avec la structure comme parametre
ClassServer::ClassServer(structThreadData&myThreadData){
m_graphic =true;
m_threadData = myThreadData;}bool ClassServer::Initialisation(){SetConsoleOutputCP(CP_UTF8);//Si nous sommes sous WINDOWSint erreur;#ifdefined(WIN32)//De plus, on doit devrez ajouter, dans le début de notre fonction main, le code suivant pour pouvoir utiliser les sockets sous WindowsWSADATAWSAData;//La fonction WSAStartup sert à initialiser la bibliothèque WinSock. La macro MAKEWORD transforme les deux entiers (d'un octet) qui lui sont passés en paramètres en un seul entier (de 2 octets) qu'elle retourne. Cet entier sert à renseigner la bibliothèque sur la version que l'utilisateur souhaite utiliser (ici la version 2,0). Elle retourne la valeur 0 si tout s'est bien passé
erreur =WSAStartup(MAKEWORD(2,2),&WSAData);#elif
erreur =0;#endifbool retVal =false;if(!erreur){// 1------------------------------------------------ Création de la socket------------------------------------------------///*Pour créer la socket, il nous faudra utiliser la fonction socket avec le prototype suivant : * int socket(int domain, int type, int protocol);*///Comme dans notre cas nous utiliserons le protocole TCP/IP, notre fonction sera toujours :
sock =socket(AF_INET, SOCK_STREAM,0);// Si la socket est valideif(sock != INVALID_SOCKET){if(!m_graphic)printf("La socket %d est maintenant ouverte en mode TCP/IP\n",(unsignedint)sock);else{pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText(QString("La socket %1 est maintenant ouverte en mode TCP/IP\n").arg((unsignedint)sock));pthread_mutex_unlock(&m_threadData.psh->mut);}// 2------------------------------------------------Paramétrer une socket------------------------------------------------///* Après avoir déclaré et créé la socket, nous allons la paramétrer *//* Configuration */// SOCKADDR_IN sin; //Déja créée ci-dessus
sin.sin_family = AF_INET;
sin.sin_addr.S_un.S_addr =htonl(INADDR_ANY);
sin.sin_port =htons(PORT);
retVal =true;}else{if(!m_graphic)perror("socket");else{pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText(QString("Erreur Socket : %1 ").arg(GetLastError()));pthread_mutex_unlock(&m_threadData.psh->mut);}}}return retVal;}//Déstructeur
ClassServer::~ClassServer(){SetConsoleOutputCP(temp);}//La fonction LISTENvoid* ClassServer::FctListenClient(void*arg){// 3------------------------------------------------Établir une connexion avec le client*------------------------------------------------///*Enfin, pour associer à la socket ces informations, nous allons utiliser la fonction : * int bint(int socket, const struct sockaddr* addr, socklen_t addrlen); */(void*)arg;int theStatus =1;//Donc, nous ferons toujours ainsi :
sock_err =bind(sock,(SOCKADDR *)&sin, recsize);/* Si la socket ne fonctionne pas*/if(sock_err == SOCKET_ERROR){//On appelle la fonction FctThreadListenClientif(m_graphic){//pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText(QString("Erreur : bind : %1 ").arg(GetLastError()));//pthread_mutex_unlock(&m_threadData.psh->mut);}}/* Si la socket fonctionne */else{// 4------------------------------------------------Établir une ecoute avec le client------------------------------------------------////Démarrage du listage (mode server)bool stopWhile =true;
sock_err =listen(sock,5);qDebug()<<" sock_err = "<< sock_err;if(m_graphic){
m_threadData.strucPlainText->appendPlainText(QString("Listage du port %1...\n").arg(PORT));}/* Si la socket fonctionne en LISTEN*/if(sock_err != SOCKET_ERROR){// On utilisera la fonction comme cela :// Attente pendant laquelle le client se connecteif(m_graphic){
m_threadData.strucPlainText->appendPlainText(QString("Patientez pendant que le client se connecte sur le port %1 ").arg(PORT));}while(stopWhile){
csock =accept(sock,(SOCKADDR*)&csin,&crecsize);//Avec "csock" représentant la socket client et "csin" son contexte d'adressage.//Si CSOCK est une invalide socketif(csock == INVALID_SOCKET){if(m_graphic){//pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText(QString("Problème de création de socket client"));//pthread_mutex_unlock(&m_threadData.psh->mut);}
stopWhile =false;}//Si CSOCK est une valide socketelse{if(m_graphic){
m_threadData.strucPlainText->appendPlainText(QString("Un client se connecte avec la socket %1 de %2 : %3\n").arg((int)csock).arg(inet_ntoa(csin.sin_addr)).arg(htons(csin.sin_port)));}}/* Si CSOCK est une valide socket */}/* la boucle WHILE */}/* Si la socket ne fonctionne pas en LISTEN*/else{if(m_graphic){
m_threadData.strucPlainText->appendPlainText(QString("Erreur Listen"));}}void*retval = nullptr;}/* Si la socket fonctionne *///return nullptr;pthread_exit((void*)(theStatus));}void ClassServer::FctSendMessage(structMsgData&msgData){
sock_err =send(csock,(char*)&msgData,sizeof(msgData),0);if(sock_err != SOCKET_ERROR ){qDebug()<<"Le message a bien était envoyé";}else{qDebug()<<"Le message n'a pas était envoyé";}}bool ClassServer::FctDeconnect(){// 4------------------------------------------------Fermer la connexion------------------------------------------------///*Finalement nous terminerons par la fonction closesocket qui permet de fermer une socket. * int closesocket(int sock); * Son prototype est très simple, je pense donc que la fonction se passe de commentaires.*///Fermeture de la socket client et de la socket serveurbool retVal =false;//Fermeture de la socket clientqDebug()<<"csock in deconnect = "<< csock;if(csock != INVALID_SOCKET){if(!closesocket(csock)){if(m_graphic){//pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText("Fermeture de la socket client.");//pthread_mutex_unlock(&m_threadData.psh->mut);}
retVal =true;}else{if(m_graphic){//pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText("La socket client n'a pas été fermée.");//pthread_mutex_unlock(&m_threadData.psh->mut);}
retVal =false;}}//Fermeture de la socket serveurif(!closesocket(sock)){if(m_graphic){//pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText("Fermeture de la socket serveur.");//pthread_mutex_unlock(&m_threadData.psh->mut);}
retVal =true;}else{if(m_graphic){//pthread_mutex_lock(&m_threadData.psh->mut);
m_threadData.strucPlainText->appendPlainText("La socket serveur n'a pas été fermée.");//pthread_mutex_unlock(&m_threadData.psh->mut);}
retVal =false;}if(!m_graphic){
m_threadData.strucPlainText->appendPlainText("Fermeture du serveur terminée.");}//Si nous sommes sous WINDOWS#ifdefined(WIN32)//Cette fonction va simplement libérer les ressources allouées par la fonction WSAStartup().WSACleanup();#endifreturn retVal;}
#include"classclient.h"SOCKETClassClient::csock =0;SOCKADDR_INClassClient::csin;socklen_tClassClient::crecsize =sizeof(csin);int ClassClient::sock_err;QPlainTextEdit*m_plainTextEdit = nullptr ;structThreadData ClassClient::m_threadDataClient;bool ClassClient::m_graphic =false;structMsgData ClassClient::m_msgDataClient;bool ClassClient::boolConnection =false;
ClassClient::ClassClient(){
m_graphic =false;Initialisation();}
ClassClient::ClassClient(structThreadData&myThreadData){
m_graphic =true;
m_threadDataClient = myThreadData;Initialisation();}
ClassClient::~ClassClient(){SetConsoleOutputCP(temp);}void ClassClient::Initialisation(){SetConsoleOutputCP(CP_UTF8);#ifdefined(WIN32)WSADATAWSAData;int erreur =WSAStartup(MAKEWORD(2,2),&WSAData);#elifint erreur =0;#endifif(!erreur){/* Création de la socket */
csock =socket(AF_INET, SOCK_STREAM,0);printf("La socket client est %d\n", csock);/* Configuration de la connexion */
csin.sin_addr.S_un.S_addr =inet_addr("127.0.0.1");//127.0.0.1"); "192.168.0.12"
csin.sin_family = AF_INET;
csin.sin_port =htons(PORT);}}bool ClassClient::FctConnection(){
m_threadDataClient.strucPlainText->appendPlainText(QString("je suis la connection"));bool returnVal =false;//Si on n'arrive pas à se connecter.if(connect(csock,(SOCKADDR *)&csin,sizeof(csin))== SOCKET_ERROR){//printf("Connexion à %s sur le port %d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));// Attente pendant laquelle le client se connecteif(!m_graphic)printf("Impossible de se connecter.");else
m_threadDataClient.strucPlainText->appendPlainText(QString("Impossible de se connecter."));
returnVal =false;}//Si on arrive à se connecterelse{if(!m_graphic)printf("Connexion à %s sur le port %d\n",inet_ntoa(csin.sin_addr),htons(csin.sin_port));else{
m_threadDataClient.strucPlainText->appendPlainText(QString("Connexion à %1 sur le port %2.").arg(inet_ntoa(csin.sin_addr)).arg(htons(csin.sin_port)));}
returnVal =true;
boolConnection =true;}return returnVal;}void ClassClient::FctDeconnection(){/* On ferme la socket précédemment ouverte */closesocket(csock);//QMessageBox::information(nullptr, "csock", QString::number(csock));#ifdefined(WIN32)WSACleanup();#endif
boolConnection =false;
m_threadDataClient.strucPlainText->appendPlainText(QString("Déconnexion"));}void* ClassClient::FctReceiveMsg(void*arg){qDebug()<<"j'attends un message avant le while";//m_msgDataClient = (MsgData*)arg;while(boolConnection){if(recv(csock,(char*)&m_msgDataClient,sizeof(m_msgDataClient),0)!= SOCKET_ERROR){
std::stringnom = m_msgDataClient.nom;
std::stringmessage = m_msgDataClient.message;qDebug()<< QString::fromStdString(nom);qDebug()<< QString::fromStdString(message);if(!m_graphic)printf("Connexion à %s sur le port %d\n",inet_ntoa(csin.sin_addr),htons(csin.sin_port));else{pthread_mutex_lock(&m_threadDataClient.psh->mut);
m_threadDataClient.strucPlainText->appendPlainText(QString(QString::fromStdString(nom)+" : "+ QString::fromStdString(message)));pthread_mutex_unlock(&m_threadDataClient.psh->mut);}}}//On arrete le thread//pthread_exit(arg);}
Je lance le serveur je me connecte et je peux envoyer des messages,
Le souci c’est du coté du client que lui reçoit les messages et j’arrive à les afficher avec des qDebug() avec cette fonction coté client
void* ClassClient::FctReceiveMsg(void*arg){qDebug()<<"j'attends un message avant le while";//m_msgDataClient = (MsgData*)arg;while(boolConnection){if(recv(csock,(char*)&m_msgDataClient,sizeof(m_msgDataClient),0)!= SOCKET_ERROR){
std::stringnom = m_msgDataClient.nom;
std::stringmessage = m_msgDataClient.message;qDebug()<< QString::fromStdString(nom);qDebug()<< QString::fromStdString(message);if(!m_graphic)printf("Connexion à %s sur le port %d\n",inet_ntoa(csin.sin_addr),htons(csin.sin_port));else{pthread_mutex_lock(&m_threadDataClient.psh->mut);
m_threadDataClient.strucPlainText->appendPlainText(QString(QString::fromStdString(nom)+" : "+ QString::fromStdString(message)));pthread_mutex_unlock(&m_threadDataClient.psh->mut);}}}//On arrete le thread//pthread_exit(arg);}
est utilisé dans les autres fonctions et qui marche très bien, comme dans cette fonction.
void ClassClient::FctDeconnection(){/* On ferme la socket précédemment ouverte */closesocket(csock);//QMessageBox::information(nullptr, "csock", QString::number(csock));#ifdefined(WIN32)WSACleanup();#endif
boolConnection =false;
m_threadDataClient.strucPlainText->appendPlainText(QString("Déconnexion"));}
Je n’arrive pas à trouver pourquoi ça plante ?
Problème n°2
Quand je connecte le serveur avec cette fonction
void MainWindowServer::SlotStartServer(){//myClassServer = ClassServer((QPlainTextEdit*)ui->plainTextEditRapport);
myClassServer =ClassServer(m_threadData);if(myClassServer.Initialisation()){//Lancement de l'écoute du serveurint ret =pthread_create(&threadListen, NULL, ClassServer::FctListenClient, nullptr);if(ret !=0){
ui->plainTextEditRapport->appendPlainText("Erreur : erreur de création de pthread_create().");}else{
ui->plainTextEditRapport->appendPlainText("Succés : Création de pthread réussie");
ui->btnLancer->setEnabled(false);
ui->btnDeconnecter->setEnabled(true);}}}
Et que je me déconnecte avec cette fonction
void MainWindowServer::SlotDeconnexion(){//pthread_cancel(threadListen);if(myClassServer.FctDeconnect()){
ui->btnDeconnecter->setEnabled(false);
ui->btnLancer->setEnabled(true);}//Just pour tester l'arret du threadif(pthread_cancel(threadListen))qDebug()<<"threadListen arreter";elseqDebug()<<"threadListen pas arreter";}
La première fois se passe très bien, mais si je me déconnecte et que j’essaye de me reconnecter plus de deux fois ça plante aussi.
Merci d’avance de votre aide je coince depuis une semaine.
mamiemando
Messages postés33535Date d'inscriptionjeudi 12 mai 2005StatutModérateurDernière intervention12 février 20257 828 22 sept. 2022 à 11:57
Bonjour,
Je pense que la question est un peu trop longue (et double) là où sur un forum on attend généralement des exemples de code minimaux illustrant un seul problème. Il faut pas mal de motivation pour copier coller un à un les fichiers, trouver comment compiler ton programme, avoir les bonnes librairies, bref ce n'est pas immédiat de reproduire le problème.
Donc pour te débloquer, et comme ça fait bien longtemps que je n'ai pas fait de Qt, je te propose de plutôt te donner quelques astuces qui te permettront de mieux comprendre ce qui cloche.
Tu peux vérifier un outil comme wireshark que les paquets de ton application circulent bien comme ils le devraient. Normalement c'est le cas si tu arrives à afficher qs_message.
Personnellement, il me paraît bizarre d'avoir besoin de pthread dans un programme Qt. Normalement l'esprit de Qt, c'est plutôt définir des événements et comment les rattraper (slots). Si le debug si dessus affiche ce qu'il faut, ça pourrait être le cause du problème.
Problème n°2
Je pense que la cause du problème est la même. Et donc je pense que le point de départ, c'est commencer par lire ceci.
Merci de me répondre, en fait pour la circulation des paquets, ils circulenet parfaitement, et comme tu le propose dans la solution, je la'i vérifié avec des qDebug, et en fait quand je supprime la ligne
Après quelques recherches, j'ai appris que objets Qt réagissent mal avec les thread provenant des autres librairies.
C'est vrai qu'avec Qt on peut tout faire et j'ai toujours fais avec, mais il m'est arrivé dans l'une de mes missions que le client ne veut pas parler de framwork et surtout pas de Qt, et c'est pour ça et ce que je voulais à la base c'est d'être indépendant de QT, ou de toute autre librairie.
mamiemando
Messages postés33535Date d'inscriptionjeudi 12 mai 2005StatutModérateurDernière intervention12 février 20257 828 22 sept. 2022 à 17:30
Bonjour Mourad,
C'est vrai qu'avec Qt on peut tout faire et j'ai toujours fais avec, mais il m'est arrivé dans l'une de mes missions que le client ne veut pas parler de framwork et surtout pas de Qt, et c'est pour ça et ce que je voulais à la base c'est d'être indépendant de QT, ou de toute autre librairie.
Tu peux utiliser libpthread dans l'absolu mais dans le cas présent, tu sembles vouloir une interface graphique, et si tu l'écris disons en GTK, tu risques de faire face au même problème... Du coup ça n'est peut être pas le bon moment de t'entraîner avec.
Du coup de deux choses l'une. Soit tu réécris ton projet pour être vraiment dans l'esprit de Qt, soit ta question sur les threads (s'il y en a une) devrait porter sur un exemple qui n'est pas basé sur Qt.
Considère-t'on le problème comme résolu (maintenant qu'il a été identifié) ?