probleme thread linux (pthread)

probleme thread linux (pthread) - C - Programmation

Marsh Posté le 17-08-2009 à 15:20:27    

Bonjour,
 
J'ai un problème de threads sous linux ubuntu 9.04.
Je code en C avec la bibliothèque <sys/socket.h>.
 
Avant de poster le code j'explique mon probleme :
Je dois réaliser un module de gestion de message (HTTP/TCP),  
et je me place entre un serveur S et un client C.
 
J'utilise LibCurl pour quelques étapes.
 
L'étape 1 consiste à réceptionner une requete HTTP de C
L'étape 2 consiste à la renvoyer à S.
S nous répond, on réceptionne la requette et on la relance a C.
 
Dans le principe, c'est simple. Mais l'on reçoit une cinquantaine de requete HTTP de C,
donc l'emploi des threads s'impose, et là c'est le drame (corruption de mémoire, etc.)
 
Il faut savoir que si je met les threads en mode JOIN, le programme marche !
Mais le but est bien entendu de lancer les 50 threads d'un coup, pas l'un a la suite de l'autre.
 
Voici la partie principale du code :
 

Code :
  1. if((socket_server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
  2. {
  3.  printf("Socket creation error !\n" );
  4.  return EXIT_FAILURE;
  5. }
  6. // nous : on écoute sur le port HTTP (80) en local
  7. sockaddr_server.sin_family = AF_INET;
  8. sockaddr_server.sin_port = htons(80);
  9. sockaddr_server.sin_addr.s_addr = inet_addr("127.0.0.1" );
  10. server_len = sizeof(sockaddr_server);
  11. client_len = sizeof(sockaddr_client);
  12. if(setsockopt(socket_server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
  13.  printf("setsockopt" );
  14.  return EXIT_FAILURE;
  15. }
  16. if(bind(socket_server, (struct sockaddr*)&sockaddr_server, server_len) < 0)
  17. {
  18.  printf("Socket bind error !\n" );
  19.  return EXIT_FAILURE;
  20. }
  21. if(listen(socket_server, 10) != 0)
  22. {
  23.  printf("Socket listen error !\n" );
  24.  return EXIT_FAILURE;
  25. }
  26. int bEnd = 0;
  27. int iThreadNumber = 1;
  28.  pthread_attr_init(&attr);
  29. pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
  30. while(bEnd == 0)//boucle infinie pour le moment
  31. {
  32.  printf("[~]Waiting connection... \n" );
  33.  socket_client = accept(socket_server, (struct sockaddr*)&sockaddr_client, (socklen_t *)&client_len);
  34.  if(socket_client > 0)
  35.  {
  36.   printf("Recu une connection... socket %i\n",socket_client);
  37.   printf("IP du client : %s\n",inet_ntoa(sockaddr_client.sin_addr));
  38.   printf("Port du client : %i\n",ntohs(sockaddr_client.sin_port));
  39.   time(&date);
  40.   printf("[!]Established connection at %s\n", ctime(&date));
  41.   // reception du paquet HTTP
  42.   nbOctects = 0;
  43.   memset(requestHTTP,0,MAX_RECEPT);
  44.   if(   (nbOctects = read(socket_client, &requestHTTP, MAX_RECEPT))    < 0)
  45.   {
  46.    printf("Socket recv error !" );
  47.    return EXIT_FAILURE;
  48.   }
  49.   requestHTTP[nbOctects] = '\0';
  50.   printf("\n" );
  51.   printf("ETAPE 1 : Requete HTTP entière : (%d octects)\n", nbOctects);
  52.   //printf("%s\n", requestHTTP);  
  53.   /////////////////////////////////////////////////////////////////////////////////////////////////////////////     
  54.   // Gestion des threads :
  55.   ex = (struct infosThread *)malloc(sizeof(struct infosThread));
  56.   memset(ex,0,sizeof(struct infosThread));
  57.   if(ex!=NULL)
  58.   {
  59.    memcpy(&(ex->iSocketTCPClient),&socket_client, sizeof(socket_client));
  60.    memcpy(&(ex->iSizeRequetteHTTP),&nbOctects, sizeof(nbOctects));
  61.    memcpy(&(ex->iThreadNumber),&iThreadNumber, sizeof(iThreadNumber));
  62.    ex->cRequetteHTTP = (char *)malloc(sizeof(char) * nbOctects);
  63.    memcpy(ex->cRequetteHTTP,requestHTTP, nbOctects);
  64.    ex->cRequetteHTTP[nbOctects]='\0';
  65.   }else{
  66.    printf("probleme structure declaration\n" );
  67.   }
  68.   //lancement de la thread (etape 2 a finale)
  69.   printf("\n" );
  70.   printf(" --------------------------------------------- \n" );
  71.   printf(" LANCEMENT THREAD NUMERO %i sur socket %i\n",iThreadNumber,ex->iSocketTCPClient);
  72.   pthread_t thread;
  73.   usleep(10); // 10 ms
  74.   rc = pthread_create(&thread, &attr, print_message_function, (void *)ex);
  75.   if(rc != 0)
  76.   {
  77.    printf ("pthread_create error for thread\n" );
  78.    exit(0);
  79.   }
  80.   else
  81.   {
  82.    iThreadNumber++;//thread suivante
  83.   }
  84.   if(ex->cRequetteHTTP !=NULL){
  85.    free(ex->cRequetteHTTP);
  86.    ex->cRequetteHTTP= NULL;
  87.   }
  88.   if(ex!=NULL){
  89.    free(ex);
  90.    ex=NULL;
  91.   }
  92.  }
  93. }


 
 
Voici la fonction threadée :
 

Code :
  1. void * print_message_function( void *ptr )
  2. {
  3. char * requestHTTP = NULL;
  4. // récupération des données :
  5. int iSocketTCPClient = 0;
  6. int iSizeRequetteHTTP = 0;
  7. int iThreadNumber = 0;
  8. memcpy(&iSocketTCPClient,&(((struct infosThread *)(ptr))->iSocketTCPClient), sizeof(int));
  9. memcpy(&iSizeRequetteHTTP,&(((struct infosThread *)(ptr))->iSizeRequetteHTTP), sizeof(int));
  10. memcpy(&iThreadNumber,&(((struct infosThread *)(ptr))->iThreadNumber), sizeof(int));
  11. printf("iSocketTCPClient : %i\n",iSocketTCPClient);
  12. printf("iSizeRequetteHTTP : %i\n",iSizeRequetteHTTP);
  13. printf("iThreadNumber : %i\n",iThreadNumber);
  14. // PARFOIS iSocketTCPClient = 0 ! BUG !
  15. if(iSocketTCPClient > 0 && iSizeRequetteHTTP >0 && iThreadNumber >0)
  16. {
  17. requestHTTP = (char *)malloc(iSizeRequetteHTTP);// ici ça change la taille du ptr
  18. memset(requestHTTP,0,iSizeRequetteHTTP);
  19. memcpy(requestHTTP,((struct infosThread *)(ptr))->cRequetteHTTP,iSizeRequetteHTTP);
  20. requestHTTP[iSizeRequetteHTTP] = '\0';
  21. printf("taille  : %i     taille : %i    taille:%i\n",iSizeRequetteHTTP,strlen(requestHTTP),strlen(((struct infosThread *)(ptr))->cRequetteHTTP));
  22. printf("cRequetteHTTP :\n %s ",requestHTTP);
  23. /////////////////////////////////////////////////////////////////////////////////////
  24. //autres variables : (gestion des fichiers temporaires suivant les threads)
  25. char REQUEST_HTTP_THREAD[40]="";
  26. char RESPONSE_THREAD[40]="";
  27. sprintf(REQUEST_HTTP_THREAD,"thread_%i_1_requetteHTTP",iThreadNumber);
  28. sprintf(RESPONSE_THREAD,"thread_%i_2_reponse",iThreadNumber);
  29. /////////////////////
  30. // lancer le processus :
  31. printf("etape2\n" );
  32. /////////////////////////////////////////////////////////////////////////////////////////////////////////////     
  33. // ETAPE 2 : renvoyer la requete vers les serveurs
  34. CURL *curl = NULL;
  35. CURLcode res = 0;
  36. int sockfd = 0;
  37. size_t iolen = 0;
  38. curl = curl_easy_init();
  39. if(curl)
  40. {
  41.  // ici on met l'IP du server
  42.  curl_easy_setopt(curl, CURLOPT_URL, IP_SERVER);
  43.  // Do not do the transfer - only connect to host
  44.  curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
  45.  res = curl_easy_perform(curl);
  46.  if(CURLE_OK != res)
  47.  {
  48.   printf("Error: %s\n", strerror(res));
  49.   //return 1;
  50.  }
  51.  // Extract the socket from the curl handle - we'll need it for waiting
  52.  res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd);
  53.  //int flags = fcntl (sockfd, F_GETFL);
  54.  //fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);  
  55.  if(res != CURLE_OK)
  56.  {
  57.   printf("Error: %s\n", strerror(res));
  58.   //return 1;
  59.  }
  60.  // wait for the socket to become ready for sending
  61.  if(!wait_on_socket(sockfd, 0, 10000L))
  62.  {
  63.   printf("Error: timeout.\n" );
  64.   //return 1;
  65.  }
  66.  printf("Sending request (thread(%i)) from TV to Server\n",iThreadNumber);
  67.  // sauvegarde de la requete dans un fichier :
  68.  FILE * pRequestHTTP = fopen(REQUEST_HTTP_THREAD, "wb" );
  69.  if(pRequestHTTP)
  70.  {
  71.   fwrite(requestHTTP,1,iSizeRequetteHTTP,pRequestHTTP);
  72.   fclose(pRequestHTTP);
  73.  }//  
  74.  // Send the requestHTTP. Real applications should check the iolen to see if all the request has been sent
  75.  res = curl_easy_send(curl, requestHTTP, strlen(requestHTTP), &iolen);
  76.  if(res != CURLE_OK)
  77.  {
  78.   printf("Error: %s\n", strerror(res));
  79.   exit(1);
  80.   //return 1;
  81.  }
  82.  printf("Reading response (thread(%i)) from Server.\n",iThreadNumber);
  83. printf("etape3\n" );
  84.  ////////////////////////////////////////////////////  
  85.  // ETAPE 3 : récupérer la réponse du serveur
  86.  int iPart = 0;
  87.  FILE * pReponse = fopen (RESPONSE_THREAD,"wb" );
  88.  char buf[MAX_RECEPT]="";
  89.  memset(buf,0,MAX_RECEPT);
  90.  if(pReponse)
  91.  {
  92.   for(;;)
  93.   {
  94.    wait_on_socket(sockfd, 1, 500L);
  95.    res = curl_easy_recv(curl, buf, MAX_RECEPT, &iolen);
  96.    fwrite(buf,1,iolen,pReponse);// en binaire !!!!!
  97.    memset(buf,0,MAX_RECEPT);
  98.    if(res != CURLE_OK){ break; }
  99.    iPart++;
  100.   }
  101.   ////////////////////////////////////////////////////
  102.   fclose (pReponse);
  103.  }
  104.  else{
  105.   printf("impossible d'ouvrir %s\n",RESPONSE_THREAD);
  106.  }
  107.  printf("Reading response (thread(%i)) from Server DONE !.\n",iThreadNumber);
  108.  curl_easy_cleanup(curl);
  109. }
  110. /////////////////////////////////////////////////////////////////////////////////////////////////////////////
  111. printf("etape Finale\n" );
  112. /////////////////////////////////////////////////////////////////////////////////////////////////////////////     
  113. // ETAPE FINALE
  114. char * cBody = NULL;
  115. int nbOctects = 0;
  116. FILE * pBodyfileRead = fopen (RESPONSE_THREAD,"rb" );
  117. // obtain file size:
  118. fseek (pBodyfileRead , 0 , SEEK_END);
  119. long lSize = ftell (pBodyfileRead);
  120. rewind (pBodyfileRead);
  121. // allocate memory to contain the whole file:
  122. cBody= (char*) malloc (sizeof(char)*lSize);
  123. memset(cBody,0,sizeof(char)*lSize);
  124. if (cBody== NULL) {fputs ("Memory error",stderr); exit (2);}
  125. // copy the file into the buffer:
  126. int result = fread (cBody,1,lSize,pBodyfileRead);
  127. cBody[lSize] = '\0';
  128. if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
  129. //pthread_mutex_lock (&my_mutex);
  130. // cBodycontiennent les informations a renvoyer a la box
  131. // on renvoie a la netgem box tv du départ :
  132. //int flags = fcntl (iSocketTCPClient, F_GETFL);
  133. //fcntl (iSocketTCPClient, F_SETFL, flags | O_NONBLOCK);  
  134. if(  (nbOctects = write(iSocketTCPClient, cBody, lSize)) < 0)
  135. {
  136.  printf("Socket send error !" );
  137.  return (void *)EXIT_FAILURE;
  138. }
  139. else
  140. {
  141.  printf("etape finale (thread(%i)): %i envoyés\n", iThreadNumber, nbOctects);
  142. /*  
  143.  struct timeval to;  
  144.  fd_set myset;
  145.  
  146.  to.tv_sec = 0;
  147.  to.tv_usec =0; //microseconde
  148.  
  149.  FD_ZERO(&myset);
  150.  FD_SET(iSocketTCPClient,&myset);
  151.  if(select(iSocketTCPClient+1,&myset,NULL,&myset,&to) == 1){
  152.   printf("EVENEMENT !\n" );
  153.   return (void *)-1;
  154.  }
  155. */
  156. }
  157. fclose(pBodyfileRead);
  158. if(cBody!=NULL){
  159.  free(cBody);
  160.  cBody=NULL;
  161. }
  162. /////////////////////////////////////////
  163. /////////////////////////////////////////////////////////////////////////////////////////////////////////////     
  164. // Nettoyage de sockets et des pointeurs :
  165. remove(REQUEST_HTTP_THREAD);
  166. remove(RESPONSE_THREAD);
  167. }
  168. printf("Close sockets (thread(%i))\n",iThreadNumber);
  169. int iCheckSocketClosedClient = close(iSocketTCPClient);
  170. iSocketTCPClient=-1;
  171. printf("Check state of sockets (0 is OK) : Client : %i\n", iCheckSocketClosedClient);
  172. if(requestHTTP!=NULL){
  173.  free(requestHTTP);
  174.  requestHTTP=NULL;
  175. }
  176. printf(" FIN THREAD NUMERO %i\n",iThreadNumber);
  177. printf(" --------------------------------------------- \n" );
  178. printf("\n" );
  179. pthread_exit ((void *)0);
  180. }

Reply

Marsh Posté le 17-08-2009 à 15:20:27   

Reply

Marsh Posté le 17-08-2009 à 15:23:10    

Quel est le problème?


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 17-08-2009 à 15:27:26    

J'ai ce genre d'erreur :
*** glibc detected *** ./main: corrupted double-linked list: 0x09fe3b58 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xb7dcb07f]
/lib/tls/i686/cmov/libc.so.6[0xb7dccb8d]
/lib/tls/i686/cmov/libc.so.6(__libc_calloc+0xef)[0xb7dce6ef]
/usr/local/lib/libcurl.so.4(Curl_open+0x36)[0xb7ef8226]
/usr/local/lib/libcurl.so.4(curl_easy_init+0x37)[0xb7f06697]
./main[0x8049aec]
/lib/tls/i686/cmov/libpthread.so.0[0xb7ec44ff]
/lib/tls/i686/cmov/libc.so.6(clone+0x5e)[0xb7e3f49e]
 
etc............
 
Parfois j'ai ça, parfois le programme se bloque (comme s'il ne recevait plus de connection...) dur dur

Reply

Marsh Posté le 17-08-2009 à 15:56:43    

Alors ce n'est sans doute pas la solution, mais je dirais que  

Code :
  1. ex = (struct infosThread *)malloc(sizeof(struct infosThread));
  2. rc = pthread_create(&thread, &attr, print_message_function, (void *)ex);
  3. if(ex->cRequetteHTTP !=NULL){
  4.    free(ex->cRequetteHTTP);
  5.    ex->cRequetteHTTP= NULL;
  6.   }
  7.   if(ex!=NULL){
  8.    free(ex);
  9.    ex=NULL;
  10.   }


c'est moyen parce que rien n'indique que le thread a eu le temps de se servir de ton pointeur avant que tu ne libères la mémoire. Tu devrais libérer dans le thread je pense.


---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 17-08-2009 à 16:11:49    

je vais essayer cette solution pourquoi pas !
 
je ne savais pas qu'il est possible de libérer dans le thread l"élément passé en paramètre

Reply

Marsh Posté le 17-08-2009 à 16:19:17    

Ton élément n'est qu'un pointeur, pas la zone mémoire en question.
Essaie:

Code :
  1. int *i=malloc (4*sizeof(int));
  2. int *z=i;
  3. free(z);


Tu verras qu'il n'y a pas de problème. z peut aussi être défini dans une autre fonction. Par contre il ne faut pas oublier de libérer la mémoire et de ne pas le faire deux fois.


Message édité par ptitchep le 17-08-2009 à 16:23:57

---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 17-08-2009 à 16:23:20    

bon ça a l'air de marcher un chouillia mieux (j'ai enlever le usleep aussi)
je traite bien une quinzaine de threads et le glibc error arrive d'un coup

Reply

Marsh Posté le 17-08-2009 à 16:26:31    

j'ai mis :
 
 // libération de la structure :
 if(((struct infosThread *)(ptr))->cRequetteHTTP !=NULL){
  free(((struct infosThread *)(ptr))->cRequetteHTTP);
  ((struct infosThread *)(ptr))->cRequetteHTTP= NULL;
 }
 
 if(ptr!=NULL){
  free(ptr);
  ptr = NULL;
 }
 
a la fin de la fonction threadé

Reply

Marsh Posté le 17-08-2009 à 16:30:57    

je ne pense pas que ce soit ça l'erreur, car si je commente les free(structure) que ce soit dans la fonction thréadé ou dans la boucle, ça plante de la même manière !

Reply

Marsh Posté le 17-08-2009 à 16:33:22    

Utilise un debugger pour trouver la ligne qui plante. Vu d'ici avec juste un bout de code et une erreur ce n'est pas facile. Surtout que honnêtement, je n'ai pas tout lu XD. Je pense que si tu arrives à trouver l'instruction qui fait tout tomber, tu pourrais identifier le problème.

 

Ca ne plantait pas parce que tu avais de la chance et que le thread copiait les infos avant qu'elles ne soient détruites.
Si ptr est NULL, tu auras une segmentation fault sur  if(((struct infosThread *)(ptr))->cRequetteHTTP !=NULL). Vérifie d'abord ptr avant ptr->cRequetteHTTP. Par contre les free sont dans le bon ordre.

 

(Je l'avais dit que ça n'était sûrement pas le problème: tu aurais eu seg fault comme message d'erreur)

 

Au fait la pile d'appels de ton message d'erreur parle de curl. Tu appelles peut-être une fonction de cette bibliothèque en lui donnant une variable avec du caca dedans...


Message édité par ptitchep le 17-08-2009 à 16:41:51

---------------
deluser --remove-home ptitchep
Reply

Marsh Posté le 17-08-2009 à 16:33:22   

Reply

Marsh Posté le 17-08-2009 à 16:47:36    

en utilsant gdb il mécrit la même chose que au dessus

Reply

Marsh Posté le 17-08-2009 à 17:38:33    

Oué, débugguage de programme multi-threadé en C, des heures de plaisir en vue :love:  
 
Le genre d'erreur que tu obtiens, ça pu à plein nez le buffer overflow, erreur assez difficile à corriger.
 
Déjà, dans ce pavé, il y a une ligne qui devrait t'arracher les yeux :

Code :
  1. requestHTTP = (char *)malloc(iSizeRequetteHTTP);// ici ça change la taille du ptr
  2. memset(requestHTTP,0,iSizeRequetteHTTP);
  3. memcpy(requestHTTP,((struct infosThread *)(ptr))->cRequetteHTTP,iSizeRequetteHTTP);
  4. requestHTTP[iSizeRequetteHTTP] = '\0';


 
Ce qui n'est pas gagné, car si tu fais ce genre d'erreur sur des trucs aussi simple, tu vas en chier pour la suite.


Message édité par tpierron le 17-08-2009 à 17:39:03
Reply

Marsh Posté le 17-08-2009 à 17:45:31    

je ne comprend pas, quelle est l"erreur mon ami ? je ne suis sans doute pas un pro comme toi :)

Reply

Marsh Posté le 17-08-2009 à 17:53:18    

Hmm, pas besoin d'être un pro pour comprendre ton erreur. Pour la débusquer au milieu du tas, il faut de l'expérience, certes. Mais maintenant que que je te l'ai montrée, tu dois comprendre ce qui ne vas pas, parce que c'est la base du C. Si tu ne veux pas faire cet effort, j'ai sincèrement peur pour la suite de ton projet.
 
Surtout que j'ai juste survolé ton code, rien ne dit que c'est l'unique erreur qu'il puisse y avoir.

Reply

Marsh Posté le 17-08-2009 à 18:01:45    

tu me serai d'une grande aide si tu pouvais m'aider.
ça fait vraiment un moment que je bloque !
j'ai beau cherché je ne comprend pas mon erreur...

Reply

Marsh Posté le 17-08-2009 à 18:14:48    

j'ai fait :
 
if(iSizeRequetteHTTP == strlen(((struct infosThread *)(ptr))->cRequetteHTTP)){
  strcpy(requestHTTP,((struct infosThread *)(ptr))->cRequetteHTTP);
}else{
  printf("probleme taille !!!!\n" );
}
 
et il arrive quon n'est pas la même taille... tu métonnes que ça narrive pas a bien copié !
mais alors pourquoi en faisant  
iSizeRequetteHTTP = ((struct infosThread *)(ptr))->iSizeRequetteHTTP;
2 lignes après ils sont différents !! ça mépate là

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed