[C++] Communication entre 2 threads

Fermé
NicoBoklo Messages postés 87 Date d'inscription samedi 2 décembre 2006 Statut Membre Dernière intervention 8 septembre 2010 - 7 avril 2008 à 23:15
 sweety - 2 févr. 2012 à 23:14
Bonjour à tous !

Je vous expose mon problème.

Je suis en train d'apprendre la programmation c++ au travers de winsock. J'ai créer (ou plutôt j'ai pomper) un code serveur qui utilise des sockets. Lorsque je lance le serveur, il attends les connections clientes. A chaque connection, il créer un thread pour le client en question. Jusque la tout va...

Le problème :

J'arrive a communiquer entre le serveur et le client... Mais pas entre 2 clients et je ne vois pas comment faire! J'ai fait des recherches en non stop cette après midi mais sans resultats...

En gros ce que je recherche pour le moment c'est créer un genre de petit chat. Je veux pouvoir communiquer d'un thread à un autre... J'ai beau chercher des solutions, je sais communiquer à travers les sockets mais alors les threads, c'est une autre paire de manche! J'avais penser à fork() et pipe() mais j'ai appris que celà est typique de linux (Une raison de plus de detester encore plus winwin ^^) De plus j'essaye de comprendre le code du serveur que j'ai récupérer mais avec le peu de doc sur les threads que j'ai trouvé, j'avance à reculons (si vous voyez ce que je veut dire ^^)

Merci à ceux qui pourrons m'aider... Désolé si ce que je dis n'a pas tjrs un sens (et oui j'apprends de moi-même) mais bon avec la programmation "pouet pouet" qu'on fait à l'iut du genre "ecrire un programme qui lit une chaine de caractère et renvoi le nombre de caractère" ou alors "taper dans le terminal 'echo blabla', On affiche quoi?", sa va 5min, après c'est petage de plombs...
A voir également:

12 réponses

NicoBoklo Messages postés 87 Date d'inscription samedi 2 décembre 2006 Statut Membre Dernière intervention 8 septembre 2010 2
8 avril 2008 à 09:43
Salut à tous les 2...

En gros vos solutions se ressemblent mais le problème est bien le "lui" dans envoi "ça" à "lui". Je pense que jusque là on est tous d'accord. Si je comprends bien, le mieux à faire sa serai un truc du genre :

- faire une liste des nom de clients avec socket associé.
- le client1 envoi au serveur le nom du destinataire et le message
- le serveur regarde la liste des client et deduit le socket
- send au destinataire grâce au socket trouver...

sinon :

- faire une liste des nom de client avec indentifiant du thread asocié
- le client1 regarde la liste et trouve l'identifiant du thread du destinataire
- ensuite communication entre thread

En gors j'vois que ces 2 solutions (Z'êtes d'accord avec moi? ^^)

Maintenant ce qui m'enerver (et oui sa m'arrive) c'est que j'ai pomper ce code sur le net (sans vraiment comprendre le principe du thread) Du coup la partie création de serveur no problems! Quand j'attaque la partie thread (aille), c'est finis. Du coup, la solution ou j'ai tendance à plus pencher, utiliser les sockets (et leur descripteur de flux). Je vais faire une chose plus simple, voila le code source : (au moins les idées serons plus claires)

Serveur.cpp :

int serveur::start (){
	SOCKADDR_IN                 ClientAddr;
	int                         ClientAddrLen;
	HANDLE                      hProcessThread;
	SOCKET                      NewConnection;
	struct thread_param         p;

	if( ( ListeningSocket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ) ) == INVALID_SOCKET ){
		cerr << "ne peut creer la socket. Erreur no " << WSAGetLastError()<< endl;
		WSACleanup();
		return 1;
	}

	if( bind( ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof( ServerAddr ) ) == SOCKET_ERROR ){
		cerr << "bind a echoue avec l'erreur " << WSAGetLastError() << endl;
		cerr << "Le port est peut-etre déja utilise par un autre processus " << endl;
		closesocket( ListeningSocket );
		WSACleanup();
		return 1;
	}

	if( listen( ListeningSocket, 5 ) == SOCKET_ERROR ){
		cerr << "listen a echoue avec l'erreur " << WSAGetLastError() << endl;
		closesocket( ListeningSocket );
		WSACleanup();
		return 1;
	}

	cout << "serveur demarre : a l'ecoute du port " << port << endl;
	running = true;
	ClientAddrLen = sizeof( ClientAddr );

	while(running){

		if((NewConnection = accept( ListeningSocket, (SOCKADDR *) &ClientAddr, &ClientAddrLen)) == INVALID_SOCKET){
			cerr  << "accept a echoue avec l'erreur " << WSAGetLastError() << endl;;
			closesocket( ListeningSocket );
			WSACleanup();
			return 1;
		}

		p.ser = this;
		p.soc = NewConnection;

		cout << "client connecte ::  IP : " <<inet_ntoa( ClientAddr.sin_addr )<< " ,port = " <<ntohs( ClientAddr.sin_port ) << endl;

		hProcessThread = CreateThread(NULL, 0,&serveur::ThreadLauncher, &p,0,NULL);
		if ( hProcessThread == NULL ){
			cerr << "CreateThread a echoue avec l'erreur " <<GetLastError()<< endl;
		}
	}

	return 0;
}

/* Thread client */

DWORD serveur::ClientThread(SOCKET soc){
/*==========Code client===========*/
}

Serveur.h :

<code>#ifndef __serveur_h__ 
#define __serveur_h__ 

#include <winsock2.h>

class serveur;

struct thread_param{
       serveur* ser;
       SOCKET soc;
};


class serveur{
	private: 
		int           port;
		SOCKET	      ListeningSocket;
		bool          running;  
		SOCKADDR_IN   ServerAddr;   
		DWORD         ClientThread(SOCKET);  
	public:                                        
		serveur(int);
		int                 init();
		int                 start ();  
		int                 pause ();
		static DWORD WINAPI ThreadLauncher(void *p){
			struct thread_param *Obj = reinterpret_cast<struct thread_param*>(p);               
			serveur *s = Obj->ser;                          
			return s->ClientThread(Obj->soc);       
		}
}; 



#endif


Voilà, je passe le main pas très utile et d'autre fonction (du genre serveur::pause()).

En gros ce qu'il me faut, pouvoir récupérer soit l'identifiant du thread lors de sa création, soit obtenir le socket créer (SOCKET soc en paramètre dans ClientThread) Première solution, je ne sais pas comment faire pour obtenir l'identifiant d'un thread. 2ème solution, je stock le socket client (dans un tableau 2D par exemple tab[pseudo][SOCKET]) du coup je me retrouve avec ma liste des clients.

Ensuite, le clients envoi le pseudo à qui il veut parler au serveur, le serveur lit dans le tableau, trouve le socket et fait un send avec le bon socket.

Désolé si c'est pas très clair ce que je dis... J'ai dû me reprendre à 2 fois mais c'est tellement flou tout sa...

Merci de votre patiente.
1
tatou_38 Messages postés 1928 Date d'inscription vendredi 21 avril 2006 Statut Membre Dernière intervention 5 août 2015 120
7 avril 2008 à 23:20
Que tu ne puisse communiquer entre deux clients c'est normal. Les deux clients ne peuvent communiquer entr'eux qu'au travers du serveur s'il est prévu pour et l'accepte.
Communiquer entre deux threads est simple, vu que tous les threads ont la mémoire de l'application en commun. Mais atention, il faut desfois protéger les accès pour éviter des effets de bords aléatoires et difficiles à dénicher.
0
NicoBoklo Messages postés 87 Date d'inscription samedi 2 décembre 2006 Statut Membre Dernière intervention 8 septembre 2010 2
7 avril 2008 à 23:54
Merci pour ta réponse aussi rapide!

Donc en gros si je comprends bien :

Client1 dit au serveur : dit "salut" à Client2.

En gros ce qu'il faut c'est :
- le client 1 envoi au serveur le message plus le destinataire.
- le serveur envoi au client 2 le message avec le nom de l'expediteur.

Concretement je code celà :

Client1:
cout<<"Entrer le destinataire : ";
cin>>destinataire;
send(socket,destinataire,sizeof(destinaire),0);
cout<<"Entrer votre message : ";
cin>>message;
send(socket,message,sizeof(message),0);


Serveur :
recv(socket,destinataire,sizeof(destinataire),0);
recv(socket,message,sizeof(message),0);

et ensuite... 


Ben la je bloque... Si je fait un send(...) j'envoi les infos directement à tous les clients (si je me trompe pas...) Comment doit-je faire pour dire envoi "ça" à "lui". Il faut déjà differencier les 2 clients (identifiant de leur thread?) et ensuite envoyer le message au bon destinataire (une fonction spécifique à sa?...)

Aussi pour mieux comprendre l'utilité d'un thread. fork equivalent à thread sauf sans le pipe (à cause de la mémoire partagée?)

Du coup sa serai peut-être plus simple de faire ça à coup de fork...
Un client se connecte, je "fork", s'il veut parler à un autre client je fais un pipe entre les 2 processus... Après la communication c'est du gateau! Mais sous winwin y'a un équivalent à sa...

Merci de ton aide.
0
tatou_38 Messages postés 1928 Date d'inscription vendredi 21 avril 2006 Statut Membre Dernière intervention 5 août 2015 120
8 avril 2008 à 08:23
Normalement le dialogue est sur initiative du client : le client envoie une question au serveur qui lui répond.
Là tu veux que le serveur envoie un message à un client précis sans que ce soit une réponse. Donc il faut que le serveur sache quel est le socket à utiliser pour faire le send.

Logiquement, le serveur crée un thread à chaque connexion, chaque thread correspond donc à un client. Il faut donc (par exemple) que tu demande au bon thread d'envoyer la sauce.

D'autre part, il faut que le client soit conçu pour traiter des messages "non sollicités", c'est à dire qui arrivent à n'importe quel moment sans être une réponse à une question.
0

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

Posez votre question
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
8 avril 2008 à 08:54
Salut, pas du tout d'accord avec tatou.
En client serveur, une fois la connexion établi, on peux dire qu'il n'y a plus de client et de serveur. tu peux faire des send ou recv dans le sens que tu veux, attention tout de même à les enchainer correctement pour faire la même chose du coté serveur que du coté client.
lorsque tu fait un send depuis le serveur, tu ne l'envoie pas à tout les clients, il suffit de spécifier le socket du client à qui tu veux l'envoyer. Lorsque tu fait socketC=accept(...) socketC est un descripteur de flux réseau vers le client qui viens de se connecter. il suffit alors de tout copier en passant ça à un thread.
Pour les thead, j'utilise pthead, qui est bien documenter sur Linux et disponible sous windows. Je le trouve relativment simple d'utilisation.
Ensuite, il faut faire la communication entre thread.
Je te cache pas qu'un point délicat c'est de savoir vers qui le serveur doit retourner le message depuis ce qu'il reçoit de son client.
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
8 avril 2008 à 10:48
hProcessThread est ton identificateur de thread.
il te reste alors à le récupérer et à l'associer avec le socket et l'ip qui vont avec dans une variable.
Je pense que tu peux utilisé maps de la STL pour ça.
Ensuite, lorsque chaque thread créé reçoit un nouveau message, il faut qu'il le renvoie au bon destinataire.
https://docs.microsoft.com/en-us/previous-versions/bb202727(v=msdn.10)?redirectedfrom=MSDN
0
NicoBoklo Messages postés 87 Date d'inscription samedi 2 décembre 2006 Statut Membre Dernière intervention 8 septembre 2010 2
8 avril 2008 à 11:08
En fait je viens de trouver une petite solution. Je vais déjà tenter de faire simple. Le serveur est senser être utile dans un jeu de poker. Du coup aucun problème pour les recv() et send() entre serveur et client puisque je connais déjà l'ordre des receptions/envois. En gros ce que je fait, je créer un tableau 2D (10 entrées) dans serveur.cpp. A chaque fois qu'un client se connecte, j'ajoute son socket dans le tableau. Une fois le tableau remplis les connections sont refusées et le jeu est lancer. Le serveur envoi à "joueur" ses cartes (je lis dans le tableau le socket du joueur) et j'utilise le socket obtenu pour faire un send.

Pour le jeu de poker, je pense que cel est plus simple, ensuite pour pouvoir créer un "t'chat", la communication entre thread serait donc mieux. Je vais un peu avancé dans ce petit serveur et vous tiens au courant de l'avancement.

Je pense que ma solution devrait marcher...

En tout cas merci à vos de m'avoir éclairé!
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
8 avril 2008 à 12:15
Le problème du chat, c'est qu'en effet, l'ordre des envoie réception n'est pas connu à l'avance.
je croix que la solution choisi dans ce cas là, c'est de faire deux thread, un pour les messages allant de A à B et un pour ceux allant de à A. Bien entendu, si il faut passer par un serveur, c'est un poil plus compliquer.
0
NicoBoklo Messages postés 87 Date d'inscription samedi 2 décembre 2006 Statut Membre Dernière intervention 8 septembre 2010 2
8 avril 2008 à 14:06
J'avais aussi penser à ce genre de schéma, en gros un thread d'ecoute et un thread d'envoi. Mais vu mes compétences en thread j'vais abandonner l'idée du "t'chat" pour passer a un schéma connue (mon jeu de poker) Par contre si tu a des liens pour des cours sur les threads il sont les bienvenues, j'en ai chercher mais je n'ai rien trouver, ou en tout cas aucun cours prennant les bases ou expliquant correctement le thread et les paramètre qu'on lui passe lors de la création! Je te remercie pour ton aide, j'ai pas mal avancer et maintenant que je sais communiquer avec un client précis, je vais attaquer la partie graphique de mon jeu, et sa c'est une autre paire de manche!
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
8 avril 2008 à 15:11
voici un petit exemple que j'ai construit en pthread.
c'est juste pour tester les thread et les variables qu'ils modifie. Je pense que les fonctions équivalent existent en thread win classique.
#include <stdio.h>
int glabol=1;
void* cal(void* x)
{
static int nn=glabol;
for(long int j=-100;j<100;j++)
for(int i=0;i<100000;i++)
	i=i+1944-3*6*9*12;
int xi=*(int*)x;
xi=xi*xi;printf("dans cal %d nb apel : %d\n",xi,nn);
//x=&xi;
*(int*)x=xi;
nn++;
glabol=xi;return x;
};
void* fonc(void* x);
int main()
	{
	pthread_t tt,uu;
	pthread_attr_t *tt_attr,*uu_attr;
	int k;
	int*x;int j=10;x=&j;
	tt_attr =new pthread_attr_t;
	pthread_attr_init(tt_attr);
        pthread_attr_setdetachstate(tt_attr, PTHREAD_CREATE_JOINABLE);//PTHREAD_CREATE_DETACHED
	printf("%d\t%d\t%d\n",*x,glabol,k);
	k=pthread_create(&tt,tt_attr,cal,x);
	printf("c'est parti\n");
	k+=2;
	cal(&k);
	glabol*=2;
	printf("%d\t%d\t%d\n",*x,glabol,k);
/*	uu_attr =new pthread_attr_t;
	pthread_attr_init(uu_attr);
        pthread_attr_setdetachstate(uu_attr, PTHREAD_CREATE_JOINABLE);//PTHREAD_CREATE_DETACHED*/
	k=pthread_create(&tt,tt_attr,cal,x);
	pthread_join(tt,NULL);
	printf("%d\t%d\t%d\n",*x,glabol,k);
	scanf("%d",&k);
	return 0;
	}
// programme modifiant bien glabol et x , x est bien l'argument passer à la fonction lors de lapel du thread
// pthread_t : unsigned long int !!
// g++ -lpthread
0
NicoBoklo Messages postés 87 Date d'inscription samedi 2 décembre 2006 Statut Membre Dernière intervention 8 septembre 2010 2
8 avril 2008 à 15:25
Merci,

Je vais regarder ce programme et tenter de comprendre comment tout marche!
0
est ce que vous pouvez publier le code client ici
0