Problème avec pthread et QPlainTextEdit

Résolu/Fermé
Mourad2009B Messages postés 104 Date d'inscription lundi 23 août 2010 Statut Membre Dernière intervention 2 juillet 2024 - Modifié le 22 sept. 2022 à 11:41
Mourad2009B Messages postés 104 Date d'inscription lundi 23 août 2010 Statut Membre Dernière intervention 2 juillet 2024 - 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

classserver.h :

#ifndef CLASSSERVER_H
#define CLASSSERVER_H

#include "header.h"
#include <QDebug>
#include <QPlainTextEdit>
#include <QString>

class ClassServer
{

private:
      unsigned temp = GetConsoleOutputCP();
      /* Socket et contexte d'adressage du serveur  */
      static SOCKET sock;
      static SOCKADDR_IN sin;
      static socklen_t recsize;

      /* Socket et contexte d'adressage du client */
      static SOCKET csock;
      static SOCKADDR_IN csin;
      static socklen_t crecsize;
      static int sock_err;

      static QPlainTextEdit *m_plainTextEdit ;
      static struct ThreadData m_threadData;
      static bool m_graphic;
      static struct MsgData m_msgDataServer;


public:
      ClassServer();
      ClassServer(struct ThreadData &myThreadData);
      ~ClassServer();
      static bool Initialisation();
      static void* FctListenClient(void *);
      static void FctSendMessage(struct MsgData &);
      static bool FctDeconnect();
};

#endif // CLASSSERVER_H

classserver.cpp

#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;
}

mainwindowserver.h

#ifndef MAINWINDOWSERVER_H
#define MAINWINDOWSERVER_H

#include <QMainWindow>
#include <QMessageBox>
#include "threadFunctions.h"

namespace Ui {
      class MainWindowServer;
}

class MainWindowServer : public QMainWindow
{
      Q_OBJECT

public:
      explicit MainWindowServer(QWidget *parent = nullptr);
      ~MainWindowServer();
      void FctConnections();

public slots:
      void SlotStartServer();
      void SlotSendMessage();
      void SlotDeconnexion();


private:
      Ui::MainWindowServer *ui;
      ClassServer myClassServer;
      //On déclare les thread
      pthread_t threadListen;
      struct Shared m_shared;
      struct ThreadData m_threadData;
      struct MsgData m_msgDataServer;

};

#endif // MAINWINDOWSERVER_H

mainwindowserver.cpp

#include "mainwindowserver.h"
#include "ui_mainwindowserver.h"

MainWindowServer::MainWindowServer(QWidget *parent) :
      QMainWindow(parent),
      ui(new Ui::MainWindowServer)
{
      ui->setupUi(this);
      m_shared =
      {
            .data = 0,
            .mut = PTHREAD_MUTEX_INITIALIZER,
            .synchro = PTHREAD_COND_INITIALIZER,
      };

      m_threadData =
      {
            .strucPlainText = ui->plainTextEditRapport,
            .strucBtnOn = ui->btnLancer,
            .strucBtnOff = ui->btnDeconnecter,
            .psh = &m_shared,
      };

      FctConnections();
}

MainWindowServer::~MainWindowServer()
{
      pthread_cancel(threadListen);
      delete ui;
      //delete myClassServer;
}

void MainWindowServer::FctConnections()
{
      QObject::connect(ui->btnLancer, SIGNAL(clicked()), this, SLOT(SlotStartServer()));
      QObject::connect(ui->btnDeconnecter, SIGNAL(clicked()), this, SLOT(SlotDeconnexion()));
      QObject::connect(ui->btnEnvoyer, SIGNAL(clicked()), this, SLOT(SlotSendMessage()));
}

void MainWindowServer::SlotStartServer()
{
      //myClassServer = ClassServer((QPlainTextEdit*)ui->plainTextEditRapport);
myClassServer = ClassServer(m_threadData);

      if(myClassServer.Initialisation())
      {
            //Lancement de l'écoute du serveur
            int 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);
            }
      }
}

void MainWindowServer::SlotSendMessage()
{
      strcpy(m_msgDataServer.nom, "Serveur");
      strcpy(m_msgDataServer.message, ui->lineEditMessage->text().toStdString().c_str());
      ClassServer::FctSendMessage(m_msgDataServer);
}

void MainWindowServer::SlotDeconnexion()
{

      //pthread_cancel(threadListen);
      if(myClassServer.FctDeconnect())
      {
            ui->btnDeconnecter->setEnabled(false);
            ui->btnLancer->setEnabled(true);
      }

      //Just pour tester l'arret du thread
      if(pthread_cancel(threadListen))
            qDebug() << "threadListen arreter";
      else
            qDebug() << "threadListen pas arreter";
}

main.cpp

#include "mainwindowserver.h"

#include <QApplication>
#include <QLocale>
#include <QTranslator>

int main(int argc, char *argv[])
{
      QApplication a(argc, argv);

      QTranslator translator;
      const QStringList uiLanguages = QLocale::system().uiLanguages();
      for (const QString &locale : uiLanguages) {
            const QString baseName = "SocketGraphicServer_" + QLocale(locale).name();
            if (translator.load(":/i18n/" + baseName)) {
                  a.installTranslator(&translator);
                  break;
            }
      }
      MainWindowServer w;
      w.show();
      return a.exec();
}

Client

classclient.h

#ifndef CLASSCLIENT_H
#define CLASSCLIENT_H

#include "../SocketGraphicServer/header.h"
#include <QDebug>
#include <QPlainTextEdit>
#include <QString>
#include <QMessageBox>

class ClassClient
{
      unsigned temp = GetConsoleOutputCP();
      /* Socket et contexte d'adressage du client */
      static SOCKET csock;
      static SOCKADDR_IN csin;
      static socklen_t crecsize;
      static int sock_err;

      static QPlainTextEdit *m_plainTextEdit ;
      static struct ThreadData m_threadDataClient;
      static bool m_graphic;
      static struct MsgData m_msgDataClient;
      static bool boolConnection;

public:
      ClassClient();
      ClassClient(struct ThreadData &myThreadData);
      ~ClassClient();
      static void Initialisation();
      static bool FctConnection();
      static void FctDeconnection();
      static void* FctReceiveMsg(void*);
};

#endif // CLASSCLIENT_H

classclient.cpp

#include "classclient.h"

SOCKET ClassClient::csock = 0;
SOCKADDR_IN ClassClient::csin;
socklen_t  ClassClient::crecsize = sizeof(csin);
int  ClassClient::sock_err;

QPlainTextEdit *m_plainTextEdit = nullptr ;
struct ThreadData ClassClient::m_threadDataClient;
bool ClassClient::m_graphic = false;
struct MsgData ClassClient::m_msgDataClient;
bool ClassClient::boolConnection = false;

ClassClient::ClassClient()
{
      m_graphic = false;
      Initialisation();
}

ClassClient::ClassClient(struct ThreadData &myThreadData)
{
      m_graphic = true;
      m_threadDataClient = myThreadData;

      Initialisation();
}

ClassClient::~ClassClient()
{
      SetConsoleOutputCP(temp);
}

void ClassClient::Initialisation()
{
      SetConsoleOutputCP(CP_UTF8);
#if defined (WIN32)
      WSADATA WSAData;
      int erreur = WSAStartup(MAKEWORD(2,2), &WSAData);
#elif
      int erreur = 0;
#endif

      if(!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 connecte
            if(!m_graphic)
                  printf("Impossible de se connecter.");
            else
                  m_threadDataClient.strucPlainText->appendPlainText(QString("Impossible de se connecter."));
            returnVal = false;
      }
      //Si on arrive à se connecter
      else
      {
            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));

#if defined (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::string nom = m_msgDataClient.nom;
                  std::string message = 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);
}

mainwindowclient.h

#ifndef MAINWINDOWCLIENT_H
#define MAINWINDOWCLIENT_H

#include <QMainWindow>
#include "threadFunctions.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindowClient; }
QT_END_NAMESPACE

class MainWindowClient : public QMainWindow
{
      Q_OBJECT

public:
      MainWindowClient(QWidget *parent = nullptr);
      ~MainWindowClient();
      void FctConnectionsObjets();
      void FctRcvMsg();

public slots:
      void SlotRcvMsg();
      void SlotConnection();
      void SlotDeconnexion();

private:
      Ui::MainWindowClient *ui;
      ClassClient myClassClient;
      //On déclare les thread
      pthread_t threadRcvMsg;
      //Les structures
      struct Shared m_shared;
      struct ThreadData m_threadDataClient;
};
#endif // MAINWINDOWCLIENT_H

mainwindowclient.cpp

#include "mainwindowclient.h"
#include "ui_mainwindowclient.h"

MainWindowClient::MainWindowClient(QWidget *parent)
      : QMainWindow(parent)
      , ui(new Ui::MainWindowClient)
{
      ui->setupUi(this);
      qDebug() << "entrée du constructeur";
      m_shared =
      {
            .data = 0,
            .mut = PTHREAD_MUTEX_INITIALIZER,
            .synchro = PTHREAD_COND_INITIALIZER,
      };

      m_threadDataClient =
      {
            .strucPlainText = ui->plainTextEditRapport,
            .strucBtnOn = ui->btnConDecon,
            .strucBtnOff = ui->btnDeconnecter,
            .psh = &m_shared,
      };

      myClassClient = ClassClient(m_threadDataClient);
      FctConnectionsObjets();
}

MainWindowClient::~MainWindowClient()
{
      delete ui;
}

void MainWindowClient::FctConnectionsObjets()
{
      QObject::connect(ui->btnConDecon, SIGNAL(clicked()), this, SLOT(SlotConnection()));
      QObject::connect(ui->btnDeconnecter, SIGNAL(clicked()), this, SLOT(SlotDeconnexion()));
}

void MainWindowClient::SlotConnection()
{
      //myClassClient = ClassClient(m_threadDataClient);
      if(myClassClient.FctConnection())
      {
            ui->btnConDecon->setText("&Déconnecter");
            SlotRcvMsg();
      }

}

void MainWindowClient::SlotRcvMsg()
{
      int ret =  pthread_create(&threadRcvMsg, NULL, ClassClient::FctReceiveMsg, nullptr);
      if(ret != 0) {
            ui->plainTextEditRapport->appendPlainText("Erreur : erreur de création de pthread_create() de \"threadRcvMsg\".");
      }
      else
      {
            ui->plainTextEditRapport->appendPlainText("Succés : Création de pthread \"threadRcvMsg\" réussie");
      }
}

void MainWindowClient::SlotDeconnexion()
{
      ui->btnConDecon->setText("&Connecter");
      myClassClient.FctDeconnection();
}

main.cpp

#include "mainwindowclient.h"

#include <QApplication>
#include <QLocale>
#include <QTranslator>

int main(int argc, char *argv[])
{
      QApplication a(argc, argv);

      QTranslator translator;
      const QStringList uiLanguages = QLocale::system().uiLanguages();
      for (const QString &locale : uiLanguages) {
            const QString baseName = "SocketGraphicClient_" + QLocale(locale).name();
            if (translator.load(":/i18n/" + baseName)) {
                  a.installTranslator(&translator);
                  break;
            }
      }
      MainWindowClient w;
      w.show();
      return a.exec();
}

Problèmes

Problème n°1

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::string nom = m_msgDataClient.nom;
                  std::string message = 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);
}

Mais quand j’essaye de les afficher avec

m_threadDataClient.strucPlainText->appendPlainText(QString(QString::fromStdString(nom) + " : " + QString::fromStdString(message)));

... le programme plante avec cette erreur :

D:/Fichiers_applications/C++/Projets_QtCreator/Test/Revis_gnrle/Travaux_pratiques/SocketGraphic/build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug/SocketGraphicClient/debug/SocketGraphicClient.exe crashed.

Pourtant le

m_threadDataClient.strucPlainText->appendPlainText(QString("Déconnexion"));

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));

#if defined (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 serveur
            int 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 thread
      if(pthread_cancel(threadListen))
            qDebug() << "threadListen arreter";
      else
            qDebug() << "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.

Merci à tous

fffffff

4 réponses

mamiemando Messages postés 33201 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 juillet 2024 7 763
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.

Problème n°1

Plutôt que d'écrire une longue ligne comme :

m_threadDataClient.strucPlainText->appendPlainText(QString(QString::fromStdString(nom) + " : " + QString::fromStdString(message)));

... fais des variables intermédiaires et affiche les.

Assure-toi que tout ce qui est gauche d'un opérateur "->" ou "." est bien défini.

QString qs_nom = QString::fromStdString(nom);
QString qs_message = QString::fromStdString(message);
QString qs = QString(nom + " : " + message)
qDebug()
  << "qs_nom = " << qs_nom << std::endl
  << "qs_message = " << qs_message  << std::endl
  << "qs = " << qs  << std::endl;
// Affiche ici les valeurs de qs_nom, qs_message, qs
m_threadDataClient.strucPlainText->appendPlainText(qs);

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.

Mourad2009B Messages postés 104 Date d'inscription lundi 23 août 2010 Statut Membre Dernière intervention 2 juillet 2024
22 sept. 2022 à 16:54

Bonjour mamiemando ,

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

m_threadDataClient.strucPlainText->appendPlainText(QString(QString::fromStdString(nom) + " : " + QString::fromStdString(message)));

le programme fonctionne très bien,.

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és 33201 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 juillet 2024 7 763
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é) ?

Mourad2009B Messages postés 104 Date d'inscription lundi 23 août 2010 Statut Membre Dernière intervention 2 juillet 2024
Modifié le 22 sept. 2022 à 17:36

Merci pour tes réponses mamiemando,

Oui je vais le considéré comme résolu, quoi que normalement on devrait mètre identifié.

Comment faire pour mettre "résolu"

Merci encore.