Thread et socket besoin de conseil

cs_kawito Messages postés 24 Date d'inscription dimanche 20 juillet 2003 Statut Membre Dernière intervention 13 décembre 2005 - 25 juil. 2003 à 21:40
sebseb42 Messages postés 495 Date d'inscription dimanche 6 juillet 2003 Statut Membre Dernière intervention 9 novembre 2007 - 27 juil. 2003 à 17:35
Salut,
je desire realiser un client/serveur TCP
le probleme est que les fonctions accept et recv sont bloquante.
donc cela bloque l'affichage etc...

mon probleme est le suivant ,je devellope un jeu en Opengl et GLUT et c++ (pour la portabilité)
GLUT est tres simple d'utilisation est surtout portable:
la creation d'une fenetre la gestion du clavier ,de la souris,etc... est tres simple par rapport a un programme realise en utilisant la MFC.
realiser un jeu en MFC en plein ecran est tres compliqué voir impossible, ne parlons pas de la gestion des evenements.

Opengl c'est genial en quelques lignes de programme comprehensible, on a vite fait de faire de la 2d ou 3d en plein ecran.

la ou j'ai un probleme c'est d'implementer un client/serveur
les fonctions accept et recv sont bloquante,donc bloque l'affichage.

2 possibilités ,soit j'utilise un thread ou WSAAsynSelect(..)

les threads, je n'ai pas trouvé d'exemple claire :
comment on declare un thread, quel sont les fonctions a utiliser, dans quel ordre etc....

WSAasyncSelect(..) utilise un HWND, un HANDLE sur la fenetre, est les message windows.
mais comme j'utilise GLUT, je ne vois pas comment reccuperer le HANDLE sur la fenetre de mon application est encore moins les message windows !

quelqu'un m'a sugerer de faire une fenetre invisible, mais je ne maitrise pas du trop la MFC, pour creer une fenetre.
peut etre y a t-il moyen de reccuperer le HANDLE de la fenetre cree avec GLUT,(car pour moi c'est une surcouche)
bref je ne sais plus vers quoi me diriger.

et les threads j'y connait rien ,je comprend a quoi ça sert ,et je pense que cela resoudrais mon probleme,mais les recherches que j'ai fais sur internet n'ont rien donné,pas d'exemple simple, aucune explication de la mise en place d'un thread. bref le mistere.

mon client/serveur fonctionne cependant.
mais je ne sais pas si ce que j'ai fais et bon au niveau de mes class et des buffers d'entre/sortie des sockets.

quelqu'un peut t-il me conseiller sur la structure de mes class et des buffer ?
quelq'un peut t-il m'aprendre a faire un thread ?

voici ce que j'ai fais :

j'ai cree une class socket (socketReseau)
une class serveur qui derive de socketReseau
une class client qui derive de socketReseau egalement

la class serveur possede une liste dynamique de type generic ici la liste est du type socketReseau, cette liste correspond aux clients qui se connecterons.
le type Liste est une liste generic ,on peut y acceder avec un iterateur ou comme un tableau Liste[0].

pour ce qui est des buffers d'entre sortie des sockets (voir class socketReseau, j'ai prevu 2 buffers un pour l'entree et un pour la sortie.

je ne sais pas encore ce qui est le mieux au niveau des buffers ,ais je fais le bon choix ?
peut etre q'un seul buffer suffirais ,et pourrais etre sur un void* !!?
chaque buffer a une taille de 1024 (voir constante)

voici ce que fais mon serveur:

initialiser winsock
demarrer serveur
ecouter

accept (bloque !, il attend une connexion d'un client, donc pas cool !)

si client accepte,

le serveur attend le nom du client
recv (bloque aussi !)

le serveur envoie son nom au client
send

voici ce que fais mon client:

initialiser winsock

connecter

le client envoie son nom au serveur
send

le client attend le nom du serveur
recv (bloque !)

je vous passe les detaille de la saisie au clavier du port de l'adresse ip du serveur, du nom du client et du nom du serveur !

BREF :
mon client se connecte correctement a mon serveur
ils echangent leurs nom ,et je fais afficher l'adresse ip de chacun a l'ecran

pour l'instant le serveur ne gere q'un seul client (voir constructeur de la class serveur.(la liste n'a q'un socket)

voici mon MAIN :
int main(int argc, char** argv)
{
int idFenetre; // Identifiant de la fenêtre principale
/*** Initialisations ***/
glutInit(&argc, argv); // Initialise la bibliothèque GLUT
initialiserFenetre(&idFenetre);
initialiserOpenGL();
initialiserVariables();
/*** Boucle principale ***/
glutReshapeFunc(rafraichir);
glutIdleFunc(callBackFonction);
glutDisplayFunc(afficher);
glutMouseFunc(souris);
glutKeyboardFunc(clavier);
glutMainLoop();
return 0;
}

et mes headers socket,serveur,client....

/************ socket.h ****************/

#ifndef __SOCKET__
#define __SOCKET__

#include
#define TAILLE_BUFFER_SOCKET 1024
#define PORT_PAR_DEFAUT 5500
#define TYPE_SOCKET_PAR_DEFAUT SOCK_STREAM
#define FAMILLE_SOCKET_PAR_DEFAUT AF_INET
#define BLOQUER_RECEPTION 0
#define BLOQUER_EMISSION 1
#define BLOQUER_RECEPTION_EMISSION 2

/**************************/
/* MA CLASSE SOCKET
/**************************/

class socketReseau
{
private :

SOCKADDR_IN socketAdresseIn;
int socketType;
int socketProtocol;
SOCKET socketConnexion;
char* bufferEntree;
char* bufferSortie;

public:

socketReseau()
{
memset(&socketAdresseIn, 0, sizeof(socketAdresseIn));
socketAdresseIn.sin_family = FAMILLE_SOCKET_PAR_DEFAUT;
socketAdresseIn.sin_port = htons(PORT_PAR_DEFAUT);
//socketAdresseIn.sin_addr.s_addr = INADDR_ANY;
socketType = TYPE_SOCKET_PAR_DEFAUT;
bufferEntree = new char [TAILLE_BUFFER_SOCKET];
bufferEntree[0] = '\0';
bufferSortie = new char [TAILLE_BUFFER_SOCKET];
bufferSortie[0] = '\0';
socketConnexion = INVALID_SOCKET;
evenementReseau = NULL;
}

~socketReseau()
{
int erreur = 0;

erreur = closesocket(socketConnexion);
if (erreur == SOCKET_ERROR)
{
cerr << "Erreur a la fermeture du socketReseau" << WSAGetLastError() << endl;
}
else
{
cout << "fermeture socketReseau reussi" << endl;
}

cout << "Destructeur Socket " << endl;
}

SOCKADDR_IN* lireSocketAdresseInRef(void) { return(&socketAdresseIn); }
SOCKADDR_IN lireSocketAdresseIn(void) { return(socketAdresseIn); }
char* lireAdresseSocketAdresseIn(void) { return(inet_ntoa(socketAdresseIn.sin_addr)); }
int lireSocketType(void) { return(socketType); }
int lireSocketProtocol(void) { return(socketProtocol); }
int lireFamilleSocketAdresseIn(void) { return(socketAdresseIn.sin_family); }
unsigned short lirePortSocketAdresseIn(void) { return(socketAdresseIn.sin_port); }
SOCKET lireSocketConnexion(void) { return(socketConnexion); }
SOCKET* lireSocketConnexionRef(void) { return(&socketConnexion); }
char* lireBufferEntree(void) { return(bufferEntree); }
char* lireBufferSortie(void) { return(bufferSortie); }
void ecrireAdresseSocketAdresseIn(char* a);
void ecrireAdresseSocketAdresseInLocal(void);
void ecrireSocketType(int t) { socketType = t; }
void ecrireSocketProtocol(int p) { socketProtocol = p; }
void ecrireSocketConnexion(SOCKET s) { socketConnexion = s; }
void ecrireFamilleSocketAdresseIn(int f) { socketAdresseIn.sin_family = f; }
void ecrirePortSocketAdresseIn(unsigned short& p) { socketAdresseIn.sin_port = htons(p); }
void ecrireBufferEntree(char* chE) { bufferEntree = chE; }
void ecrireBufferSortie(char* chS) { bufferSortie = chS; }
int bindSocket(void);
int creerSocket(void);
int envoyer(void);
int recevoir(void);
void fermerSocket(void);
int bloquerSocket(void);
};

/*** Initialiser la version de Winsock et demarre Winsock ***/
int initialiserWinsock(void);

#endif
/************ socket.h ****************/

/**************************/
/* MA CLASSE SERVEUR
/**************************/
le type Liste est une liste dynamique sur un type quelconque
elle derive de socketReseau

/************ serveur.h ****************/
#ifndef __SERVEUR__
#define __SERVEUR__

#include "listeGenerique.h" // Listes génériques
#include "listeTemplate.h" // Listes templates
#include "socket.h"

#define SERVEUR_PRET true
#define SERVEUR_INDISPONIBLE false

class serveur : public socketReseau
{
private :

bool status;
int connexionMax;
Liste<socketReseau> listeSocketsClients;

public:

serveur()
{
status = false;
connexionMax = 5;
listeSocketsClients.creerListe(socketReseau(), 1); // ici je cree une cellule socketReseau destine a recevoir le client
}
~serveur();
void creerListeSocketsClients(int nbC)
{
listeSocketsClients.creerListe(socketReseau(), nbC);
}
bool lireStatus(void) { return(status); }
void ecrireStatus(bool s) { status = s; }
char* AdresseIpDuClient(int numC);
int envoyerClient(int numC, char* chOut);
char* recevoirClient(int numC);
int envoyerClients(char* chOutAll);
int recevoirClients(char* chInAll);
int demarrerServeur(unsigned short & port, int socType, int socFam);
int ecouter(void);
int attenteConnexionsClients(void);
void arreterServeur(void);
};

#endif
/************ serveur.h ****************/

/**************************/
/* MA CLASSE CLIENT
/**************************/

#ifndef __CLIENT__
#define __CLIENT__

#include "socket.h"

class client : public socketReseau
{
private :

char* adresseIPServeur;

public:
client(void) { }
~client(void) { }

char* lireAdresseIPServeur() { return(adresseIPServeur); }
void ecrireAdresseIPServeur(char* ch) { adresseIPServeur = ch;}

int connexionServeur(char* adresseIPServeur, unsigned short & port, int socType, int socFam);
int Connecter();
void arreterClient();
};

#endif
/************ serveur.h ****************/

voici un aperçu du serveur.cpp

/*** Destructeur ***/
serveur::~serveur()
{
int k = 0;
int erreur = 0;

erreur = closesocket(listeSocketsClients[k].lireSocketConnexion());
if (erreur == SOCKET_ERROR)
{
cerr << "Erreur Fermeture Socket Client pendant l'arret du serveur :" << WSAGetLastError() << endl;
}
WSACleanup(); // arret de winsock
listeSocketsClients.detruireListe();
}

int serveur::demarrerServeur(unsigned short & port, int socType, int socFam)
{
int erreur = 0;
this -> ecrireFamilleSocketAdresseIn(socFam);
this -> ecrireSocketType(socType);
this -> ecrireAdresseSocketAdresseInLocal();
this -> ecrirePortSocketAdresseIn(port);
this -> creerSocket();
this -> bindSocket();
if (this -> lireSocketConnexion() != INVALID_SOCKET)
{
cout << "SOCKET SERVEUR CREE AVEC SUCCES" << endl;
cout << "ADRESSE : " << lireAdresseSocketAdresseIn() << endl;
cout << "PORT : " << lireSocketAdresseIn().sin_port << endl;
return(0);
}
else
{
cerr << "ERREUR LORS DE LA CREATION DU SOCKET SERVEUR" << endl;
erreur = closesocket(this -> lireSocketConnexion());
if (erreur == SOCKET_ERROR)
{
cerr << "Erreur fermeture socket serveur lors du demarrage suite a une erreur a la creation : " << WSAGetLastError() << endl;
WSACleanup(); // arret de winsock
return(INVALID_SOCKET);
}

return(INVALID_SOCKET);
}
return(0);
}

/*** Mise en ecoute du serveur ***/
int serveur::ecouter(void)
{
if (listen(this -> lireSocketConnexion(), 0) == 0)
{
cout << "PASSAGE EN ECOUTE DU SERVEUR REUSSI" << endl;
return(0);
}
else
{
cerr << "ERREUR LORS DU PASSAGE EN ECOUTE DU SERVEUR : " << WSAGetLastError() << endl;
}
return(SOCKET_ERROR);
}

/*** Attendre la connexion du client ***/
int serveur::attenteConnexionsClients(void)
{
int tailleAdresse = 0;

tailleAdresse = sizeof(struct sockaddr_in);
listeSocketsClients[0].ecrireSocketConnexion(accept(this -> lireSocketConnexion(), (SOCKADDR*)listeSocketsClients[0].lireSocketAdresseInRef() ,&tailleAdresse));
if (listeSocketsClients[0].lireSocketConnexion() != INVALID_SOCKET)
{
cout << "CONNECTION CLIENT ACCEPTEE" << endl;
cout << "ADRESSE DU CLIENT : [" << listeSocketsClients[0].lireAdresseSocketAdresseIn() << "]" << endl;
return(0);
}
else
{
cerr << "ERREUR LORS DU L'ACCEPTATION DU CLIENT : %d\n" << WSAGetLastError() << endl;
return(SOCKET_ERROR);
}
return(0);
}

merci d'avance !!

kawito

6 réponses

cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
25 juil. 2003 à 22:09
Salut,

La version avec thread sera plus compliquée a mettre en oeuvre (synchronisation...) mais sera globalement mieux, surtout si tu gere plusieurs clients.

La version avec WSAAsyncSelect est plus simple (car implementée dans une fonction callback d'une fenetre) mais necessite une fenetre pour recevoir des messages. Si tu n'a pas de handle je pense que tu peux faire une fenetre invisible spetialement destinée a capter et traiter les evenements reseaux.
A toi de voir...
0
sebseb42 Messages postés 495 Date d'inscription dimanche 6 juillet 2003 Statut Membre Dernière intervention 9 novembre 2007 1
26 juil. 2003 à 16:06
salut

je suppose que seul ton client possede une interface graphique OpenGL, ton serveur n'en as pas si ?

multithread avec OpenGL, tu va morfler seveire, je veux pas te miner le moral mais... ou sinon n'utilise pas glut, car glut gere automatiquemet ses callback vers les fonctions utiles, tu ne pourra pas gere les events toi meme, donc les threads vont plus foutre le bordel qu'autre choses, je pense... :/

sinon pour les thread, soit tu decide de ne pas etre portable et tu choisit la simpliciter avec _beginthread, soit tu choisit encore de souffrir, et tu te lance dans la lib pthread :)

sinon pour le reseau, tu peux utiliser select pour rendre tes filedescriptor non-bloquant :)

voila, hesite pas a demander encore des trucs si tu as besoin de plus de details :)

a++
0
cs_kawito Messages postés 24 Date d'inscription dimanche 20 juillet 2003 Statut Membre Dernière intervention 13 décembre 2005
27 juil. 2003 à 01:21
ben si, mon client et mon serveur son en opengl,
c'est un jeu en 2d + scene 3d, et en reseau enfin j'y travaille
j'ai deja develloper ce jeu sous mac os9 en c, et j'ai cherchais longtemps a develloper ce jeu sur pc.
et je me suis vite rendu compte que faire un jeu en plein ecran sur un pc , c'est pas une mince affaire :
en gros le plein ecran est possible sous dos ,mais point de vu graphisme sous dos faut pas trop en demander !
sous mfc avec la GDI ,tu peux faire une fenetre avec barre de menu,mais pas de fenetre graphique en plein ecran.
c'est pour ça que DirectX a ete cree.
maintenant j'avais le choix de choisir DirectX ou Opengl,et pour des raisons de portabilité,ne pas oublier que Opengl est implementer sous mac OSX,terminer l'os proprietaire d'apple.
j'ai donc choisi de faire ça en Opengl et c++ pour pouvoir porter mon jeu sur OSX et Linux.
je m'efforce de rester portable, pour ce qui est du reseau ,j'utilise winsock v1.1 qui est tres proche des sockets sous linux,a part les fonction WSA le principe est le meme.

pour l'instant mon programme est portable a 100%.

de 2 à 6 joueurs, les joueurs jouent chacun leur tour,donc je pense que la gestion du reseau ne devrait pas etre tres compliqué.
j'ai choisi une architecture client /serveur TCP SOC_TREAM,AF_INET,chaque client se connecte au serveur,et le serveur gerera les clients.

Opengl possede effectivement une fonction de CallBack :
glutIdleFunc(callBackFonction);
donc je me suis dit cool ,je vais mettre ma fonction accept dans la callback,et hop le tour est joué !
mais pas cool le accept bloque toujours ,cela ne fonctionne pas .
donc obligé de faire un thread ou une fonction comme WSAAsyncSelect.

Qu"est ce que la LIB pthread pthread.lib ?
tu pourrais men dire plus sur _beginthread ?
j'aimerais bien comprendre comment faire un thread quel sont les fonctions utilisé lse Librairie et les headers a inclure ?

je n'est pas eut le temps de tester de faire une fenetre invisible windows Sous GLUT et WSAAsyncSelect,ça marche peut etre qui sait ?,mais je met un bemol.

si tu peut m'expliquer le principe des threads ,je suis preneur,car ya pas grand chose sur le net et parfois faut voir les sources que je trouve, le code est a chier ,je te dit pas le dechiffrage à faire avant de comprendre.et des doc en francais sur les socket et les thread ça cours pas les rues.

si tu peut eclairer mes lanternes !

merci d'avance.

kawito
0
sebseb42 Messages postés 495 Date d'inscription dimanche 6 juillet 2003 Statut Membre Dernière intervention 9 novembre 2007 1
27 juil. 2003 à 04:22
tout d'abbord sil te plais, essaye d'arreter d'etre flou... excuse moi mais je ne suis pas vraiment sure, au final, de comprendre reelement ce que tu veux...

de plus, essaye d'arreter d'en balancer 10000 couches quand tu postes, generalement, les messages doivent etre les plus claires et les plus concits possible...

le tu me raconte presque le synopsys de ton jeux, tu me parle des technologies employers, des techniques, tu me raconte ce que tu fait, mais ca m'interesse pas trop trop au final...

excuse mais c'etais a dire :)

bon mnt, pour les threads, ce n'est pas si simple que ca, il ne suffit pas d'inclure un header et d'appeler 3 fonctions, c'est un poil plus compliquer que ca

aussi, pour repondre a ta question "Qu"est ce que la LIB pthread pthread.lib ?"

je ne peux rien te repondre de mieux que... google est ton ami, serieux la tu demande direct alors que tu pourrais chercher un peux, cela n'est pas dur

"ya pas grand chose sur le net"
heu...

"des doc en francais sur les socket et les thread ça cours pas les rues."
un guerrier ne part pas a la conquete du monde avec un cur-dent...

je ne sais pas si tu voit l'analogie, mais bon, si tu parle pas anglais, tu peux t'en prendre qu'a toi meme, et si tu le parle, et bien il te faudra faire des efforts de traduction, ca je suis desoler, mais c'est pareil pour tout le monde

sinon pour conclure je pense (et je crois pas etre le seul a penser ca) que glut, c'est cool pour etre portable, mais alors point de vue performance, c'est la misere, et je le deconseille fortement pour un jeu, de plus tu va etre emmerder au point de vue de tes gestions d'events pour les deplacement du joueur a cause de glut, et ceci n'est qu'un bref exemple, attend-toi a pire

enfin bref, je pense quil serais plus judicieux que tu fasse des recherches sur les thread tout d'abbord, et ensuite reviens poser des question plus concise que "explique moi les threads"

ps : si tu cherche un code pour utiliser OpenGL sans glut sous Windows, tu peux chercher sur le site OPENGL_WIN32_BASE, il fonctionne tres bien, le code est tres performant, et il est tres propre, tu ne pourra pas te plaindre de la qualiter du code :)

voila, a la prochaine
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_kawito Messages postés 24 Date d'inscription dimanche 20 juillet 2003 Statut Membre Dernière intervention 13 décembre 2005
27 juil. 2003 à 16:28
desole pour le flou, il est vrais que je m'egare de la question.:)

je n'ai pas trop de probleme en anglais mais je suis deçu qu'il n'y ais pas plus de doc en francais c'est tout !
mais c'est pour ça que ce forum existe non ?

creer une fenetre invisible sous GLUT ,ça ne marche pas, car tu as besoin d'une winproc pour traiter les evenements etc...
donc autant tout faire en MFC et oublier GLUT.
pour l'instant je n'ai pas de probleme de performance avec GLUT, je suis conscient que c'est une surcouche, mais quand mes animations clignoterons je me poserais des questions.
sans carte 3d qui supporte opengl,j'ai fait des tests et la on voit clairement le probleme de perf.

j'ai donc essaye de faire un thread , et la ça fonctionne a merveille l'accept ne bloque plus :
if ((threadIDAccept _beginthread(fonctionThreadAcceptConnectionClient, 4096, (void *)NULL)) (unsigned long) - 1
{
cerr << "Impossible de cree le thread accept connexion clients, erreur = " << errno << "\n" << endl;
}

c'est pas plus dur que ça de faire un thread ?

apres,je me suis posé la question ,quand est ce que mon thread ce termine ou comment de detruire ?
je pense que le thread ce termine soit quand la fonction du thread se termine d'elle meme,soit on met un timer.

bon ça fonctionne ,je vais voir pour optimiser tout ça, et me pencher sur les flux de données entre client et serveur et decider d'un protocole de communication.

je te remerci pour ton aide.
0
sebseb42 Messages postés 495 Date d'inscription dimanche 6 juillet 2003 Statut Membre Dernière intervention 9 novembre 2007 1
27 juil. 2003 à 17:35
> donc autant tout faire en MFC et oublier GLUT.

c'est un peu ce que je te conseillais :)

> pour l'instant je n'ai pas de probleme de performance avec
> GLUT, je suis conscient que c'est une surcouche, mais
> quand mes animations clignoterons je me poserais des
> questions.

dsl, et le prend pas mal, mais c'est clairement l'attitude quil ne faut surtout pas avoir... pour le moment ca marche, c'est cool, avec cette attitude, c'est quand tu sera sur le point de finaliser ton jeu que tu te rendra compte quil faudra que tu le re-ecrive presque from-scratch, je t'assure, j'ai une extremement mince experience dans le developpement de jeu, mais je pense que c'est suffisent pour pouvoir t'affirmer que pour developper un jeu, il faut le faire tout de suite le mieux possible, sinon apres tu passe ta vie dans des modifs et des debugs infernaux... crois moi

ne jamais se dire : "la je commence comme un porc, m'en fout, on vera plus tard quand ca marchera..." apres pour tes modifs vers un truc plus performant, tu derouille seveire

> sans carte 3d qui supporte opengl,j'ai fait des tests et la
> on voit clairement le probleme de perf.

ouais mais la c'est normal, si OpenGL trouve pas de carte, il gere tout en software donc forcement, la c'est le CPU qui mange tout le boulot, et il est pas fait pour ca

> j'ai donc essaye de faire un thread , et la ça fonctionne a
> merveille l'accept ne bloque plus :
>> if ((threadIDAccept _beginthread(fonctionThreadAcceptConnectionClient, 4096, (void *)NULL)) (unsigned long) - 1
> {
> cerr << "Impossible de cree le thread accept connexion clients, erreur = " << errno << "\n" << endl;
> }

accept est toujours bloquant, le thread n'as rien changer vis-a-vis de ca, c'est juste que accept est bloquant dans un nouveau thread, ce qui ne bloque pu le thread principal...

> c'est pas plus dur que ça de faire un thread ?

avec _beginthread... non, mais attention, il y a une difference immense entre lancer des threads, et gerer ses threads

> apres,je me suis posé la question ,quand est ce que mon
> thread ce termine ou comment de detruire ?

la ou tu lance ton thread, la thread principal continue, si'il arrive a la fin du main, ton programme meurt, ainsi que tout les autres threads lancer...

mnt ton nouveau thread est lancer sur une fonction (premier parametre de la fonction _beginthread) et ce thread mourrira lorsqu'il arrivera a la fin de cette fonction

> je pense que le thread ce termine soit quand la fonction du thread se termine d'elle meme,soit on met un timer.

je voit pas trop le delire avec le timer mais bon :) enfin oui, tu peux temporiser et faire creuver le thread apres, depuis le main thread :)

jet un coup d'oeil aux fonctions WaitForSingleObject et WaitForMultipleObjects, elle pourrais s'averer forte utiles pour toi :)

je vais poster un client qui n'as que pour but de tester et de maltraiter des serveurs :), que j'ai fait avec la libtcp que j'ai ecrite et poster recement, et avec des threads, ca te donnera un bon exemple d'utilisation de base des threads

> je te remerci pour ton aide.

de rien, c'est toujours un plaisr :)
0
Rejoignez-nous