Communication serveur/client, dans les 2 sens, sans attente en UDP

[Résolu]
Signaler
Messages postés
527
Date d'inscription
lundi 15 octobre 2007
Statut
Membre
Dernière intervention
10 octobre 2013
-
Messages postés
571
Date d'inscription
vendredi 30 décembre 2005
Statut
Membre
Dernière intervention
12 juillet 2012
-
Bonjour à tous,

J'aimerais faire un programme C++ qui puisse établir une communication entre 2 ordinateurs (sous Windows) en utilisant winsock2, et en mode UDP.

Mon problème est que la communication doit se faire dans les 2 sens, de manière très rapide, et je ne peux donc pas bloquer le serveur ou le client en attente de réception de données.

J'ai donc cherché sur le net, et j'ai vu qu'il y avait plusieurs propositions:
 - utiliser des sockets non bloquantes
 - utiliser des threads
 - utiliser select.

N'ayant jamais utilisé ces solutions, je voulais vous demander d'après vous, quelle solution serait la plus adaptée à mon problème, et si vous connaissez des tutoriaux ou des exemples de codes que je pourrais étudier pour réaliser mon programme.

Merci infiniment à tous.

 Pourapprendretoujoursplus!

9 réponses

Messages postés
1115
Date d'inscription
mercredi 15 juin 2011
Statut
Membre
Dernière intervention
6 mai 2021
4
Salut,

Bah euh ... Dans tous les cas tu es obligé de faire des threads.

Il y a plusieurs solutions plus ou moins viables surtout dans le cas multi clients.

Du côté client, tu crées un thread de réception avec socket bloquante, comme ca ton programme n'est pas bloqué.

Coté serveur 1 seul client :
Et du côté serveur tu crées un thread qui accepte la connexion du client et apres tu attends les données du client.

Coté serveur plusieurs clients:
Et du côté serveur tu crées un thread qui accepte la connexion des clients. Et tu crées un thread de réception par client.

Le select est pas mal aussi. Avec le select tu crées un seul thread pour la reception et l'acceptation de tous tes clients, c'est le gros avantage en multi client.

Des exemples ou tuto, tu en as à la pelle sur le Net. En regardant ici (en priorité  ), codeguru ou codeproject, tu ne peux trouver que ton bonheur.

Pour les sockets non bloquantes, je crois qu'il faut faire du pooling pour récupérer les données, donc c'est pas top.
Messages postés
527
Date d'inscription
lundi 15 octobre 2007
Statut
Membre
Dernière intervention
10 octobre 2013
1
Oui, je comprends.

Je voyais le truc comme ça en fait:

[code]

//Code pour le 1er thread
DWORD WINAPI PremierThread ( LPVOID IpvThreadParam )
{
...
}


//Code pour le 2ème thread

DWORD WINAPI SecondThread ( LPVOID IpvThreadParam )

{

...

}

void main ( )
{

//TODO: si client, envoyer un message au serveur pour qu'il ait toutes les données nécesaires pour envoyer lui aussi des messages, et attendre une réponse ou réenvoyer un mesage. (Si pas de réponse au bout d'un certain temps, arrêter...)
//TODO: côté serveur du TOTO précédent.

//Séparation des tâches: réception/émission

HANDLE hThreads [ 2 ] ;
DWORD dwThreadId ;
DWORD dwThreadParam = 1 ;

// création des Threads

hThreads [ 0 ] = CreateThread ( NULL, NULL, PremierThread, &dwThreadParam, 0, &dwThreadId ) ;
hThreads [ 1 ] = CreateThread ( NULL, NULL, SecondThread, &dwThreadParam, 0, &dwThreadId ) ;

// attente de fin d'exécution des Threads (la thread principale attend la réponse des 2 threads créées donc)
WaitForMultipleObjects ( 2, hThreads, TRUE, INFINITE) ;

// destruction des Threads
CloseHandle ( hThreads [ 0 ] ) ;
CloseHandle ( hThreads [ 1 ] ) ;
}

[\code]

Sachant que c'est sur des machines tournant sur XP, j'utilise l'API windows.
Si pour c'est bête, ou autre, n'hésite pas à m'en faire part :)

 Pourapprendretoujoursplus!
Messages postés
56
Date d'inscription
vendredi 14 avril 2006
Statut
Membre
Dernière intervention
1 février 2009

pour le serveur

/*************************************
Multi-threaded WebServer
**************************************/

#include <stdio.h>
#include <winsock.h>
#include <windows.h>

unsigned long webserv(unsigned fd) {
      int n;
      char *header="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
      char data[512];
      char *sbuffer;
      char filename[256];
      long filesize;
      int bytes_read;
      FILE *f;
      n=recv(fd,data,512,0);              // recieve the request using fd
      data[n]=0;                          // NUL terminate it
      sscanf(data,"GET /%s ",filename);   // get the name of the file
      f=fopen(filename,"rb");             // open the file (might be binary)
      send(fd,header,strlen(header),0);   // send the header
                                          //
                                          // send the file
            if (f==NULL) {
                printf("File does not exit.\r\n");
                }
             else
              {
               // obtain file size.
               fseek (f,0,SEEK_END);    //indicate to end of file
               filesize = ftell (f);    //return the offset
               rewind (f);                    //redirect f to begining
              
               // allocate memory to contain the whole file.
               sbuffer = (char*) malloc (filesize);
            
               if (sbuffer == NULL)   printf("memory error.\r\n");
               else{
                      printf("Sending %s, size: %ld.\r\n",filename,filesize);
                      while (!feof(f)){
                                  if ( (bytes_read = fread (sbuffer,1,filesize,f)) > 0)
                                          send(fd,sbuffer,bytes_read,0);
                                  }//end of while
                      printf("Completed.\r\n");
                }//end of else
            
             free(sbuffer);
             fclose(f);
             } //end of else

closesocket(fd);
return 0;
}

int main (){
    int s;
    //n;
    unsigned SOCK;
   DWORD ThreadId;
   struct sockaddr sin={AF_INET,{31,144,0,0,0,0,0,0,0,0,0,0,0,0}};
   WSADATA wsaData;
   WSAStartup(MAKEWORD(1, 1), &wsaData);  // initialise windows sockets
   s=socket(PF_INET,SOCK_STREAM, 0);      // create a socket
   bind(s,&sin, sizeof(sin));             // bind it to port 80
   listen(s,10);                          // allow up to 10 incoming connections
    //HANDLE ThreadHandle;                         
   while(1) {
      SOCK=accept(s,NULL,NULL);                // wait for a request
       CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)webserv,(LPVOID)SOCK,0,&ThreadId);
      }
      /*ThreadHandle=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)webserv,(LPVOID)SOCK,0,&ThreadId);
      if(ThreadHandle!=NULL){
        WaitForSingleObject(ThreadHandle,INFINITE);
        CloseHandle(ThreadHandle);     For single thread, second can start until first completed */
}
Messages postés
527
Date d'inscription
lundi 15 octobre 2007
Statut
Membre
Dernière intervention
10 octobre 2013
1
Remarque: je viens de lire que le point 3 ("select") était pour si il y avait plusieurs clients et prendre les données émises par un des clients quand elles arrivent. Ce n'est donc pas adapté à mon problème (un seul client, avec des émissions et des réceptions de données dans les 2 sens).

 Pourapprendretoujoursplus!
Messages postés
527
Date d'inscription
lundi 15 octobre 2007
Statut
Membre
Dernière intervention
10 octobre 2013
1
Merci pour cette réponse fregolo52.

Pour le serveur et le client, il me faut donc 2 threads chacun? (un pour la réception, et un pour l'émission?)
Je laisse donc un thread (un sur le serveur, et un sur le client) en mode réception, socket bloquant, et l'autre thread (d'émission donc) dans une boucle?
Messages postés
527
Date d'inscription
lundi 15 octobre 2007
Statut
Membre
Dernière intervention
10 octobre 2013
1
Yop, j'ai avancé en attendant des réponses, j'arrive maintenant à créer et gérer plusieurs threads à la fois (dans un pgme), et l'UDP à sens unique (dans un autre programme)
Je merge les deux quand j'arrive chez moi, normalement ça devrait bien marcher en faisant un thread qui s'occupe de la réception de messages, et un thread qui s'occupe de l'envoi de données :)

Merci encore pour les réponses fregolo52 et tout le monde d'ailleurs :)

 Pourapprendretoujoursplus!
Messages postés
1115
Date d'inscription
mercredi 15 juin 2011
Statut
Membre
Dernière intervention
6 mai 2021
4
Pas besoin de thread pour l'émission, ca peut etre dans le thread principal du programme. send ou sendto n'est généralement pas bloquant contrairement à recv
Messages postés
527
Date d'inscription
lundi 15 octobre 2007
Statut
Membre
Dernière intervention
10 octobre 2013
1
Merci flachback, mais le code que tu donnes correspond à un serveur qui envoie un fichier quand celui ci a été demandé par un client (et il gère plusieurs clients).
Le problème que j'avais est que le serveur peut agir comme un client (ie: envoyer des données quand bon lui chante, pas seulement quand un client lui fait une demande :) )

Je pense que je vais donc rester sur mon ti pseudo-code :)

Par contre, un énorme merci car ton code m'a permis de voir comment gérer plusieurs clients à la fois (je n'en ai pas l'utilité pour mon programme actuel mais ça me sera sûrement utile ;-)  ) et aussi l'envoi de fichiers (très utile :D)

Donc encore une fois merci infiniment pour toutes ces infos :)

 Pourapprendretoujoursplus!
Messages postés
571
Date d'inscription
vendredi 30 décembre 2005
Statut
Membre
Dernière intervention
12 juillet 2012
3
Salut,

Depuis quand les threads sont obligatoires?

Les programmeurs expérimentés les évitent car ils rendent le code complexe. Ils préfèrent le multiplexage d'entrées sorties asynchrone qui lui est miraculeusement portable à deux ou trois exceptions dans le FD_SET.