Créer tableau de socket en C

Résolu/Fermé
raito75 - 10 août 2010 à 03:58
 raito75 - 14 août 2010 à 00:12
Bonjour,
J'ai déjà fais des recherches à ce sujet, mais néanmoins, la seule chose que j'obtienne avec c'est des gens qui veulent transférer un tableau par des socket et non faire un tableau de socket.
J'utilise la librairie pthread et je veut que mon serveur puisse envoyer quelque chose à tout les client connectés au serveur.
Merci d'avance d'avance.


5 réponses

Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
10 août 2010 à 16:03
Bon, j'ai trouvé ma réponse à ce problème spécifique, j'avais oublié de mettre AF_INET dans la struct sockaddr_in.
Toujours est-il que comme je pense que ça peut être un bon exemple pour le sujet initial de la discusion, je mets mon code ici.
#include <sys/socket.h>
#include <netinet/in.h>// pour sockaddr_in et htons
#include <arpa/inet.h> // pour inet_addr
#include <unistd.h> // pour close
#include "threadJL.h"// pour creer un thread joignable en une ligne.
#include <stdio.h>
#include <iostream>
#include <vector>
#include <string>
int *x,*tmp,ncores=0,s,so[10],sUDP;
const bool APPEL_STATE=false;
const int portTCP=2224,portPOST=2225,portINFO=2226;
//__SOCKADDR_ARG sai;
sockaddr_in sai,sao,saUDP;

void* recoit(void*);
void* envoie(void*);
void infoS(void);
void lserveur();
void lclient();
void postage();
void connection(std::string,bool =APPEL_STATE);
void connectionIP(std::string,bool =APPEL_STATE);
std::string IPtoUser(in_addr);
std::string infoIP(std::string);

std::string user,cores[10];
std::vector<std::string> avec;
typedef void* (*f_gen)(void*);
int main(int argc,char** argv)
	{
	if(argc<2) {std::cout<<"usage : chat USER\n";return 1;}
	user=argv[1];
	// THREAD
	x=new int;*x=0;tmp=new int;
	// SOCKET
	s=socket(AF_INET,SOCK_STREAM,0);
	sai.sin_family=AF_INET;sai.sin_port=htons(portTCP);/*inet_addr();*/sai.sin_addr.s_addr=htonl(INADDR_ANY);
	bind(s,(sockaddr*)&sai,sizeof(sockaddr));
	sao.sin_family=AF_INET;
	// DEBUT BOUCLE
//	thread((f_gen)&postage);
	thread((f_gen)&infoS);
	lclient();
	lserveur();
	//std::cin.getline(buff,sizeof buff);
	shutdown(so[0],SHUT_RDWR);
	return 0;
	}
void lserveur()
	{
	listen(s,10);
	socklen_t *taille;
	taille=new socklen_t;
	*taille=int(sizeof(sockaddr));
	// DEBUT BOUCLE
	while(*x<9)
		{std::cout<<"Debug : x "<<*x<<std::endl;
		so[*x]=accept(s,(sockaddr*)&sao,taille);std::cout<<"Debug : connection entrante "<<so[*x]<<std::endl;
		*tmp=*x;
		avec.push_back(IPtoUser(sao.sin_addr));
		thread(recoit,tmp);
		(*x)++;
		}
	}
void lclient()
	{
	thread(envoie,x);
	// SOCKET
	socklen_t *taille;
	taille=new socklen_t;
	*taille=int(sizeof(sockaddr));
	// DEBUT BOUCLE
	//connect(s,(sockaddr*)&sai,*taille);
	//pthread_join(tt,NULL);
	}
void* recoit(void* x)
	{
	int cc=*((int*)x),tmp;
	char*c=new char[512];
	while(1){
	tmp=recv(so[cc],c,512,0);
	if(tmp<=0){
		std::cout<<"problème de reception, reçu :"<<tmp<<" de "<<cc<<", errno "<<errno<<std::endl;
		/*so[cc]=0;*/  return 0;}
	std::cout<<avec[cc]<<"~"<<cc<<" dit : "<<c;
	}
	}
void* envoie(void* x)
	{
	std::string c;
	while(1)
		{
		std::getline(std::cin,c);
		if(c.find("call ")==0)
			connection(c.substr(5,c.size()));//on recupere uniquement le nom utilisateur
		else
		if(c.find("IP ")==0)
			connectionIP(c.substr(3,c.size()));//on recupere uniquement l IP
		else
		if(c=="who")
			if(ncores!=0)for(int i=0;i<ncores;++i) std::cout<<cores[i]<<std::endl;else std::cout<<"\tpersonne\n";
		else
		for(int i=0;i<*(int*)x;i++)
			if(so[i]!=0){c+="\r\n";send(so[i],c.c_str(),c.size()+1,0);}
		}
	}
void postage()
	{
	char* IP,*Broadcast="########";//"192.168.0.255";
	sUDP=socket(AF_INET,SOCK_DGRAM,0);
	saUDP.sin_family=AF_INET;saUDP.sin_port=htons(portPOST);saUDP.sin_addr.s_addr=inet_addr(Broadcast);
	bind(sUDP,(sockaddr*)&saUDP,sizeof(sockaddr));
	IP=new char[16];strcpy(IP,inet_ntoa(sai.sin_addr));
	sendto(sUDP,(user+" "+IP).c_str(),strlen((user+" "+IP).c_str()),0,(sockaddr*)&saUDP,sizeof(sockaddr));
	delete []IP;
	close(sUDP);
	sUDP=socket(AF_INET,SOCK_DGRAM,0);saUDP.sin_family=AF_INET;saUDP.sin_port=htons(portPOST);saUDP.sin_addr.s_addr=htonl(INADDR_ANY);
	bind(sUDP,(sockaddr*)&saUDP,sizeof(sockaddr));
	while (1)
		{std::cout<<"Debug : postage...\n";
		IP=new char[64];socklen_t *taille=new socklen_t(sizeof(sockaddr));
		recvfrom(sUDP,IP,64,0,(sockaddr*)&saUDP,taille);if(std::string(IP).find(user)!=std::string::npos)continue;
		cores[ncores]=IP;
		std::cout<<"nouveau venu : "<<IP<<std::endl;
		strcpy(IP,inet_ntoa(sai.sin_addr));
		sendto(sUDP,(user+" "+IP).c_str(),strlen((user+" "+IP).c_str()),0,(sockaddr*)&saUDP,sizeof(sockaddr));
		ncores++;
		delete[] IP;
		delete taille;
		}
	}
void infoS(void)
	{
	char IP[64],*buf;
	int l;
	strcpy(IP,inet_ntoa(sai.sin_addr));
	sUDP=socket(AF_INET,SOCK_DGRAM,0);
	sUDP=socket(AF_INET,SOCK_DGRAM,0);saUDP.sin_family=AF_INET;saUDP.sin_port=htons(portINFO);saUDP.sin_addr.s_addr=htonl(INADDR_ANY);
	bind(sUDP,(sockaddr*)&saUDP,sizeof(sockaddr));
	while (1)
		{std::cout<<"Debug : info demon...\n";
		buf=new char[512];socklen_t *taille=new socklen_t(sizeof(sockaddr));
		l=recvfrom(sUDP,buf,512,0,(sockaddr*)&saUDP,taille);buf[l]=0;
		cores[ncores]=std::string(buf)+" "+inet_ntoa(saUDP.sin_addr);ncores++;
		sendto(sUDP,user.c_str(),strlen(user.c_str())+1,0,(sockaddr*)&saUDP,sizeof(sockaddr));
		delete[] buf;
		delete taille;
		}
	}

void connection(std::string c,bool appel)
	{
	socklen_t *taille=new socklen_t(sizeof(sockaddr));
	for(int i=0;i<ncores;i++)
		{
		if(cores[i].find(c+" ")!=std::string::npos)
		//if(cores[i]==c)
			{
			sao.sin_addr.s_addr=inet_addr(cores[i].substr(cores[i].find(" ")+1,32).c_str());
			sao.sin_port=htons(portTCP);
			so[*x]=socket(AF_INET,SOCK_STREAM,0);
			connect(so[*x],(sockaddr*)&sao,*taille);
			if(appel)so[*x]=accept(s,(sockaddr*)&sao,taille);
			thread(recoit,x);
			avec.push_back(c);
			(*x)++;
			}
		}
	}
void connectionIP(std::string c,bool appel)
{
	int test;
	socklen_t *taille=new socklen_t(sizeof(sockaddr));
	sao.sin_family=AF_INET;
	sao.sin_addr.s_addr=inet_addr(c.c_str());
	sao.sin_port=htons(portTCP);
	so[*x]=socket(AF_INET,SOCK_STREAM,0);
	test=connect(so[*x],(sockaddr*)&sao,*taille);if(test<0) std::cout<<"erreur de connect IP : "<<test<<" "<<errno<<std::endl;
	if(appel)so[*x]=accept(s,(sockaddr*)&sao,taille);
	*tmp=*x;
	avec.push_back(c);
	thread(recoit,tmp);
	(*x)++;
}
std::string IPtoUser(in_addr IP)
{
	std::string cIP=inet_ntoa(IP);
	for(int i=0;i<ncores;++i)
		if(cores[i].find(cIP)!=std::string::npos)
		     return cores[i].substr(0,cores[i].find(" "));
	return infoIP(cIP);
	return std::string("inconnu");
}
std::string infoIP(std::string IP)
{
	char buf[512];
	int l;
	socklen_t *taille=new socklen_t(sizeof(sockaddr));
	sUDP=socket(AF_INET,SOCK_DGRAM,0);
	saUDP.sin_family=AF_INET;saUDP.sin_port=htons(portINFO);saUDP.sin_addr.s_addr=inet_addr(IP.c_str());
	sendto(sUDP,user.c_str(),strlen(user.c_str())+1,0,(sockaddr*)&saUDP,sizeof(sockaddr));
	l=recvfrom(sUDP,buf,512,0,(sockaddr*)&saUDP,taille);buf[l]=0;
	cores[ncores]=std::string(buf)+" "+IP;ncores++;
	close(sUDP);
	return std::string(buf);
}

Le truc n'est pas fini (le broadcast ne fonctionne pas), mais j'arrive à communiquer, mais des fois ça plante sans raison. Code de retour erreur (echo $?) : 141.
C'est sous Linux, pas de problème avec les MFC ;-)
1
Nabla's Messages postés 18203 Date d'inscription mercredi 4 juin 2008 Statut Contributeur Dernière intervention 28 avril 2014 3 192
10 août 2010 à 16:13
mouais, j'ai pris l'habitude de développer avec ca en cours... faut vraiment que je me prenne par la peau des fesses pour faire des sockets pur en C (et aussi bosser plus souvent sur linux), car mon serveur ira bien pou rles débuts, mais devra etre tout reprogrammé par la suite. parce qu'en effet, si un jour ca tourne vraiment, ce sera sur du linux, pas windows .... idem pur le client: veux pas obliger les gens à installer le framework .net !
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
10 août 2010 à 08:15
Salut.
Ce n'est pas toi qui a déjà posé cette question ?
Je ne vois pas où est le problème, essaie de mieux expliciter là où tu bloques.
un tableau de socket, c'est simple, ce que renvoi socket est de type sock_t, alors :
sock_t tab_socket[N];
est un tableau de socket.
0
Nabla's Messages postés 18203 Date d'inscription mercredi 4 juin 2008 Statut Contributeur Dernière intervention 28 avril 2014 3 192
Modifié par Nabla's le 10/08/2010 à 09:44
juste un petit truc, si ton serveur doit envoyer des info à tous les clients (il devra y faire client par client, mais bon, ca roule). tu utilises des threads. Peut etre que chaque client a son thread. Toi tu vas utiliser un thread différent pour envoyer l'info à tout le monde (d'après ce que j'imagine)....
fai sattention à ce que 2 threads n'utilisent pas en meme temps une meme socket, tu pourras avoir des gros bugs, voir un plantage total du serveur.
N'oublies donc pas de ergarder si tu n'as pas besoin d'un petit mutex par socket .... ou d'un mutex pour le tableau.. Enfin, comme disais joey starr "tu vois, quoi !"

aparte: char snipper, ta signature est géniale
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
10 août 2010 à 12:01
Salut Nabla's,
tu viens peut être de trouver la solution à mon problème.
Je programme un petit chat (pour le plaisir), j'ai donc un thread qui lit l'entrée standard, et dès que j'écris quelque chose je l'envoie (par send) à tout les gens connectés.
J'ai ensuite autant de thread que j'ai de gens connectés, chaque thread fait un recv() bloquant.
Mais si je demande à me connecter à quelqu'un le recv ou send qui suit plante (erreur 107 ou "Relais brisé").
Penses tu que ça en soit la cause ?
0
Nabla's Messages postés 18203 Date d'inscription mercredi 4 juin 2008 Statut Contributeur Dernière intervention 28 avril 2014 3 192
Modifié par Nabla's le 10/08/2010 à 12:13
honnetement, je sais pas, il faudrait voir le code, savoir avec quels librairies tu code..
Perso j'ai des pb aussi un serveur (entre autres de chat, mais surtout autres choses, je bosse en VC++6 / avec MFC), et j'ai eu des gros pb avec les sockets MFC multiples, les evenements, etc.... du coup, j'ai commencé à tout passer en C# parce que j'avais du mal à comprendre les erreurs (les MFC sont, d'après un conférencier microsoft, à mettre à la poubelle).

Si tu veux, on ouvre un sujet pour ton problème, ou on peux en parler en PV si t'as pas envie de mettre tout ton code publique ...

Sinon, plutot que d'envoyer tous les message directement par la socket du client (en parcourant un tableau), pourquoi ne pas utiliser une ressource "globale" dans laquelle les threads de gestion des sockets de travail iraient piocher pour l'envoyer directement aux interessés ? idem, gerré avec un semaphore, ou un thread, ou une classe perso basé sur un mutex permettant de sécuriser l'écriture, mais d'uatoriser un acces en lecture par plusieurs threads simultanés ?

Perso, j'ai pas posé les question ici parce que je me rend bien compte que seul une petite partie des membres sont a mème de répondre à mes question.... donc il y a des points sur lesquels je reste à bosser seul chez moi.

au fait, il en en C ton prog? C++ ? utilisation de librairies spécifiques ?
0
Bonjour,
Je regardais pour sock_t tab_sock[n]; mais la variable pour mon socket est déclarée "SOCKET maSocket;" avec donc le types SOCKET. (d'après ce que j'avais vu dans je ne sais plus quel tutorial).
le type sock_t me donne une erreur ('sock_t' does not name a type)
C'est plutôt la mon problème.
Je présume aussi qu'il faudra que je fasse un tableau pour ma variable pthread_t.
EN fait c'est très bête mis je pense que je réussirais sans problème le reste, mais je bloque à la déclaration !
Merci.
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
12 août 2010 à 08:11
j'ai mis sock_t pour dire le type de socket, car je ne me souvenait plus du nom : sous Linux, le type SOCKET n'existe pas.
https://broux.developpez.com/articles/c/sockets/ pour des ref et des idées.
Je comprends mieux ton problème, tu es un débutant en C...
pour déclarer un tableau, c'est :
TYPE nom_variable[taille];

où TYPE est le type des éléments dans le tableau (SOCKET ou pthread_t), nom_variable est le nom de ta variable, et "taille" est la taille de ton tableau.
0
Nabla's Messages postés 18203 Date d'inscription mercredi 4 juin 2008 Statut Contributeur Dernière intervention 28 avril 2014 3 192
12 août 2010 à 09:32
si tu es débutant, fais attention à la taille du tableau: il n'y a aucun controle pour voir si tu écris en dehors du tableau ;)
0
A vrai dire, oui je suis plutôt débutant en C, j'avais d'ailleur tester "SOCKET masocket[12];" (12 st mis strictement au hasard).
Et donc à mon avis sa respecte "TYPE nom_variable[taille];" ou SOCKET est le type, masocket le nom et 12 la taille.

Nabla's => Etant donnée que je ne sais pas combien de clients ouvrirons, donc combien la taille du tableaux sera, je préciserais un chiffre plutôt haut voir impossible à dépasser et fairais une conditions au cas ou sa dépasse.

Merci de m'aider, c'est vrai que je n'est peut-être pas le niveau exact pour le socket, mais j'avais envie de m'y intéresser et de m'y mettre tôt (néanmoins je ne comprend vraiment pas pourquoi me déclaration de socket marche pas).

PS : Merci pour le lien je lis sa.
0
Nabla's Messages postés 18203 Date d'inscription mercredi 4 juin 2008 Statut Contributeur Dernière intervention 28 avril 2014 3 192
13 août 2010 à 09:23
il y a aps de niveau minimum pour utiliser les sockets ;) c'est une partie très interessante de la programmation, que j'aime particulièrement

mais il faut avoir des bases pour le reste de l'application ....
plutot que de faire un tableau, as tu pensé à utiliser une liste chainée ?

tu fais une structure comme ceci:
struct client
{
SOCKET socket_client;
client *suivant;
};

tu as un pointeur qui fait le début de la liste, un autre pointeur que tu déplace de client en client, et le dernier client doit avoir "suivant" à null (tu te sert de ca pour savoir qui est le dernier client)

avec la liste chainée, pas besoin de s'aventurer dans un redimentionnement de tableau, pas besoin de savoir combien de client... une fois le mecanisme en place, ca marche aussi bien pour 10 que 30000 clients :D
0

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

Posez votre question
Salut,
Voila, j'ai réussie ce que je voulais faire, merci beaucoup à vous deux de m'avoir consacrée du temps, c'est sympa.
Bonne soirée, au revoir.
0