Mourad2009B
Messages postés121Date d'inscriptionlundi 23 août 2010StatutMembreDernière intervention25 avril 2025
-
Modifié le 22 sept. 2022 à 11:41
Mourad2009B
Messages postés121Date d'inscriptionlundi 23 août 2010StatutMembreDernière intervention25 avril 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
#if defined (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 suit
typedef int socklen_t;
#elif defined (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
#define closesocket(s) close(s)
// De même
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#endif
struct Personnage
{
char nom[256];
char prenom[256];
int age;
};
typedef struct Personnage Personnage;
struct Shared
{
int data;
pthread_mutex_t mut;
pthread_cond_t synchro;
};
struct ThreadData
{
QPlainTextEdit *strucPlainText;
QPushButton *strucBtnOn;
QPushButton *strucBtnOff;
struct Shared *psh;
};
struct MsgData
{
char nom[256];
char message[256];
};
#endif // HEADER_H
#include "classserver.h"
SOCKET ClassServer::sock = 0;
SOCKADDR_IN ClassServer::sin;
socklen_t ClassServer::recsize = sizeof(sin);
SOCKET ClassServer::csock = 0;
SOCKADDR_IN ClassServer::csin =SOCKADDR_IN();
socklen_t ClassServer::crecsize = sizeof(csin);
int ClassServer::sock_err = 0;
QPlainTextEdit* ClassServer::m_plainTextEdit = nullptr;
struct ThreadData ClassServer::m_threadData;
bool ClassServer::m_graphic = false;
struct MsgData ClassServer::m_msgDataServer;
//Constructeur par défaut
ClassServer::ClassServer()
{
m_graphic = false;
}
//Constructeur avec la structure comme parametre
ClassServer::ClassServer(struct ThreadData &myThreadData)
{
m_graphic = true;
m_threadData = myThreadData;
}
bool ClassServer::Initialisation()
{
SetConsoleOutputCP(CP_UTF8);
//Si nous sommes sous WINDOWS
int erreur;
#if defined (WIN32)
//De plus, on doit devrez ajouter, dans le début de notre fonction main, le code suivant pour pouvoir utiliser les sockets sous Windows
WSADATA WSAData;
//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;
#endif
bool 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 valide
if(sock != INVALID_SOCKET)
{
if(!m_graphic)
printf("La socket %d est maintenant ouverte en mode TCP/IP\n", (unsigned int)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((unsigned int)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 LISTEN
void* 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 FctThreadListenClient
if(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 connecte
if(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 socket
if(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 socket
else
{
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(struct MsgData &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 serveur
bool retVal = false;
//Fermeture de la socket client
qDebug() << "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 serveur
if(!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
#if defined (WIN32)
//Cette fonction va simplement libérer les ressources allouées par la fonction WSAStartup().
WSACleanup();
#endif
return retVal;
}
mamiemando
Messages postés33650Date d'inscriptionjeudi 12 mai 2005StatutModérateurDernière intervention30 avril 20257 846 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és33650Date d'inscriptionjeudi 12 mai 2005StatutModérateurDernière intervention30 avril 20257 846 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é) ?