IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Threads & Processus C++ Discussion :

Problème MT sur sockets.


Sujet :

Threads & Processus C++

  1. #1
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut Problème MT sur sockets.
    Bonsoir,

    Je code en ce moment un serveur TCP pour un MMORPG.
    Ayant comme idée en tête que même si je ne suis pas vraiment bon en C++, je veux une bonne synchronisation entre ce que un joueur enverra, et que tout les joueurs sur la même map recoivent ce que le serveur aura à transmettre, en même temps.

    Voici une esquisse (Picasso n'est-il pas?): http://imageshack.us/photo/my-images/194/37628850.png/

    Mon serveur a comme architecture:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    - LoginServer
    Ecoute les connections, il sert a ce que les joueurs s'identifie, gère les banIP, choix du World, et du channel, et de son personnage.
    - WorldServer
    Il contient X channel, et est unique (nom). Il recoit le client apres connection au login, pour alleger ce dernier (je pense ?)
    Il doit egalement gerer une PARTIE des packets recus par les clients
    - Channel Server
    Ce serveur gère le "in-game". Deplacements, attaques, blablatage.
    Chaque serveur, possede son propre thread.
    (Je pense que c'est une meilleure option que d'avoir 1 thread par client, car si jamais j'ai 5000 clients, je pense avoir un probleme, non ? Corrigez moi si c'est mieux d'avoir des milliers de threads, merci)

    Donc je reviens à mon image.
    Les clients vont envoyer des centaines de packets en meme temps.
    Comment est-il possible de faire, pour que certains packets soient gerer par Login, d'autres par world, et d'autres par channel ?
    Imaginons 1000 joueurs:
    • - LoginServer: 1000 joueurs, mais une gestion de TRES TRES peu de packets
    • - WorldServer: 500 joueurs (si j'en ai deux par exemple.), Avec plus de packets.
    • - Channel Server: 250 (2.) Ici, c'est un peu comme la centrale fukushima avec un tsunami en face.

    Exemple:
    • Je suis sur mn perso, je me deplace à gauche => Gestion par Channel X (X etant en fonction du channel sur lequel je me trouve)
    • Je quitte ma guilde => Gestion par World.
    • Je me deconnecte => Gestion par Login



    Voici le LoginServer, quand il démarre (JE n'ai fait aucun test de connexions, car je ne RECV pas encore sur le serveur.)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    XServer::LoginServer::LoginServer() {
    	running = true;
    	_nbClients = 0;
    	_acceptClients = true;
    	bool stop = false;
    	WSADATA WSAData;
        WSAStartup(MAKEWORD(2,0), &WSAData);
    	if((sock = socket(AF_INET, SOCK_STREAM, 0)) == (unsigned int)INVALID_SOCKET) {
    		cout << "ERROR: Could not start LoginServer" << endl;
    		stop = true;
    	}
    	SOCKADDR_IN sin;
        sin.sin_family = AF_INET;
        sin.sin_port = htons(LS_PORT);
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
        if(bind(sock, (SOCKADDR *) &sin, sizeof(sin)) == SOCKET_ERROR && !stop) {
    		cout << "ERROR: LoginServer couln'd bind socket parameters" << endl;
    		stop = true;
    	}
        if(listen(sock, 5) == SOCKET_ERROR && !stop) {
    		cout << "ERROR: LoginServer couln'd listen on port " << LS_PORT << endl;
    		stop = true;
    	}
    	if((Lthread = CreateThread(NULL, 0, ls_Listen, this,0, NULL)) == NULL && !stop) {
    		cout << "ERROR: LoginServer thread starting failed (ls_Listen)" << endl;
    		stop = true;
    	}
    	/*
    	if((Rthread = CreateThread(NULL, 0, ls_Recv, this,0, NULL)) == NULL && !stop) {
    		cout << "ERROR: LoginServer thread starting failed (ls_Recv)" << endl;
    		stop = true;
    	}
    	*/
    	if(!stop) {
    		cout << "INFO: LoginServer is up and listening on port " << LS_PORT << endl;
    	}
    }

    Et voici la fonction ls_Listen:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    DWORD WINAPI ls_Listen(LPVOID para) {
    	XServer::LoginServer *ls = (XServer::LoginServer *) para;
        SOCKADDR_IN csin;
        SOCKET csock;
        int size =  sizeof(csin);
        while(1) {
    		csock = accept(ls->sock, (SOCKADDR *) &csin, &size);
    		if(!ls->acceptingClients()) {
    			int a = CONNECT_REFUSED;
    			send(csock, (char*)&a, sizeof(int), 0);
    			shutdown(csock, 2);
    		}
    		else if(ls->getMaxClients() <= ls->nbClients()) {
    			int a = SERVER_FULL;
    			send(csock, (char*)&a, sizeof(int), 0);
    			shutdown(csock, 2);
    		}
    		else {
    			if(csock >= 0) {
    				printf("LoginServer: Client connection (IP: %s; Port: %d)", csin.sin_addr, csin.sin_port);
    				Client *c = new Client((std::string)inet_ntoa(csin.sin_addr), (int)ntohs(csin.sin_port));
    				c->Sock(csock);
    				ls->AddClient(c);
    				Packet *p = new Packet();
    				p->setInfo(1, 0);
    				c->writePacket(p);
    			}
    			else {
    				shutdown(csock, 2);
    			}
    		}
    	}
    }
    La ou je bug un peu, c'est que pour la fonction recv, il faut préciser le socket. Et je pense que c'est couteux de faire une boucle (de meme que cest trop couteux de demarrer 1000 threads.

    Donc, au final, j'aimerais pouvoir demarrer dans un premiers temps un thread dans LoginServer (ce qui est commenté) qui recevra les messages, sans pour autant faire une mega boucle sur 1000 connections.

    -------------------------------------

    Cette partie, est à oublier
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    				Client *c = new Client((std::string)inet_ntoa(csin.sin_addr), (int)ntohs(csin.sin_port));
    				c->Sock(csock);
    				ls->AddClient(c);
    Etant donné que j'avais pas encore réfléchi au probleme posé...

    Sans etre chiant (un peu quand meme), je ne suis pas vraiment bon. une expliquation pro = je vais rien comprendre


    Merci d'avance à ceux qui aurant le courage de m'aider

    nico.

  2. #2
    screetch
    Invité(e)
    Par défaut
    select() ou poll() sont des fonctions qui bloquent en attendant que quelque chose arrive sur une des sockets que tu leur donne

  3. #3
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Comment je peux utiliser ca sut 5-6 thread differents en prenant en commpte que mes socket se trouvent dans Clients::_clients?

  4. #4
    screetch
    Invité(e)
    Par défaut
    tu ne peux pas utiliser ca dans plusieurs threads sur les mêmes sockets, il faut une séparation de tes sockets. Je n'ai pas bien compris ton architecture, mais ton client (ta socket) ne peut être qu'a un endroit a la fois; soit c'est le LoginServer soit le WorldServer, etc. autrement il n'y a pas moyen que ca marche.

    Un exemple d'une architecture qui fonctionnerait mieux: un thread s'occuppe uniquement d'écouter les connections (les 1000 connexions, oui). il utilise donc listen() ou poll() et dés que quelque chose se passe sur une connexion, il fait ce qui suit:
    - reception du bourzouf dans un buffer
    - si le buffer est pas fini (en gros tu a recupéré la moitué de l'info) attendre
    - envoie le buffer dans une pile de choses a faire
    - passe au suivant

    ensuite, d'autres threads s'occuppent de traiter les demandes; a chaque fois qu'un buffer est empilé, un thread le dépile et fait le traitement correspondant, selon le type de paquet recu.

    ainsi:
    - tu maintiens un thread qui s'occuppe bien des connexions (en terme de réseau)
    - tu utilises tes threads au mieux; si personne ne déguilde, ton thread WorldServer a rien a faire. aussi, si tu as une machine avec plus de coeurs, tu crées simplement plus de threads qui vont traiter les paquets




    en règle générale on évite désormais les modèles multithreads ou les threads ont une tâche précise; on préfère les modèle "Workers" ou chaque thread peut faire n'importe quel type de job puis se rendormir, ce qui permet d'utiliser au mieux la machine.

    par contre tu vas avoir des problèmes d'accès multithreads aux resources partagées.

  5. #5
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Hmmm. Daccord. C'est une solution aussi.
    Donc du style:

    j'ai mon thread ls_Listen qui accepte les connexions.
    Il me faut un thread qui receptionne les données ? (*)
    Et ce dernier, en fonction du premier byte recu, ajoutera le packet dans (au pif.) une des 3 piles disponibles.
    Ces trois piles sont gérées alors par 3 threads differents, qui font le necessaire et renvoient le bordel aux autres clients, c'est ca ?


    (*) le thread:
    DWORD WINAPI ls_Recv(LPVOID para) {
    // il faudrait que para soit de type Packet *p. (qui est ma classe pour les messages (structure))
    // Comme ca, je parse de quoi ca parle, et je balances a mes autres potes thread.
    }

    Dans mon premier post, j'ai ma fonction/thread ls_Listen qui accepte les clients.
    Comment utiliser poll(), ou select(), dans cette fonction (Ou autre part dailleurs) pour que la performance soit optimale avec 1000 clients qui m'envoient des packet type echange IRC. Mais en beaucoup beaucoup plus rapide.

  6. #6
    screetch
    Invité(e)
    Par défaut
    tu es tres tres contraint par ton propre modèle objet, c'est pas forcément ce qui est le mieux

    donc en gros je sais pas si tu as regardé la fonction select (ou poll), si tu as pas regardé va voir avant de lire.

    en gros select va attendre qu'il se passe quelque chose sur une des sockets et te renvoyer quelle socket a un événement.
    Lorsque tu sais qu'il s'est passé quelque chose sur cette socket, tu vas pouvoir traiter la requete. La ou il y a un avantage c'est que tu SAIS que quelque chose c'est passé, je ferais donc ainsi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    for(ever):
    * select sur toute tes sockets
    * sur la socket qui a une activité: read
    * si tu as un paquet entier, mets la dans une file de paquets a traiter
    donc tu as un thread qui ne fait que select et lire le paquet, tu ne peux pas faire plus efficace dans un sens (si c'est trop de chsoes pour ton processeur, 'est simplement trop pour ton réseau aussi, y'a pas de miracle)

    ensuite tu as n threads (n > 1, preferable n = nombre de processeurs - 1) qui attende qu'un truc se passe dans la file (avec un semaphore)
    lorsque quelque chose est ajouté dans la file, ils le depilent, et font le traitement
    ces threads sont génériques, ils ne sont PAS des WorldServer ou des trucs comme ca. Ca n'a pas de sens d'avoir un WorldServer et un ChannelServer.
    Si la requete est une requete World, tu peux demander au WorldServer de la traiter; si tu as une requete de type Channel, tu peux demander au ChannelServer de la traiter (si ca t'amuse, même si je trouve que ca n'a pas forcément beaucoup de sens)

    la différence principale ici c'est que le WorldServer pourrait traiter deux requetes World au même moment sur deux threads différent. Ou plus, d'ailleurs.

    ensuite tu as un thread qui va juste attendre avec accept pour attendre de nouvelles connexions, celui la n'a pas grand chose a faire en général (il y a des joueurs qui entrent et sortent, sur les 1000, mettons 1 par minute...)



    ensuite

  7. #7
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Bonjour
    Oui jai lu le select et poll, je vais faire ca tout a lheure.
    Cest quoi semaphore?
    Comment faire pour que dans une pile ( array ?? )mon programme detecte un nouvel ajout et le traite?
    Merci, nico

  8. #8
    screetch
    Invité(e)
    Par défaut
    Un semaphore est une primitive de synchronisation (comme un mutex ou une section critique); c'est en gros un entier.
    Les threads le "prennent" ou le "rendent", c'est a dire decrémentent l'entier de 1 ou l'augmentent de 1

    ici l'entier correspond au nombre de tâches a effectuer (au nombre de requetes dans la pile quoi). Lorsque les threads veulent "effectuer" une tâche (la dépiler) ils doivent d'abord "prendre" le semaphore (le decrémenter). Si le semahore vaut 0, ca veut dire que la pile est vide, donc que le thread doit attendre (le semaphore met le thread en pause jusqu'a ce que la valeur soit au moins 1). Lorsqu'une tâche arrive sur le réseau, le thread qui lit les données sur le réseau va mettre une tâche dans la file, puis "donner" le semaphore ce qui va reveiller un thread, qui va pouvoir effectuer la tâche.


    aussi, ta pile doit être partagée donc il faut qu'elle soit thread safe. Pense bien a ca.

  9. #9
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Hmm je vois, enfin, je sais pas trop encore mais je verrais.
    D'abord je fais le recv bien proprement !

    En utilisant beej's guide, j'ai fais ceci:

    Le thread:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    DWORD WINAPI ls_Listen(LPVOID para) {
    	XServer::LoginServer *ls = (XServer::LoginServer *) para;
        SOCKADDR_IN csin;
        SOCKET csock;
        int size =  sizeof(csin);
    	int i;
    	int j;
    	int nbytes;
    	char *buf;
     
    	while(1) {
    		ls->FD_Recv = ls->FD_Listen;
    		if (select(ls->FD_Max+1, &ls->FD_Recv, NULL, NULL, NULL) == -1) {
    			cout << "ERROR: ls_Listen --> select()" << endl;
    			// stop thread
    		}
    		for(i = 0; i <= ls->FD_Max; ++i) {
    			if (FD_ISSET(i, &ls->FD_Recv)) {
    				if (i == ls->sock) {
                        csock = accept(ls->sock, (SOCKADDR *) &csin, &size);
    					if(!ls->acceptingClients()) {
    						int a = CONNECT_REFUSED;
    						send(csock, (char*)&a, sizeof(int), 0);
    						shutdown(csock, 2);
    					}
    					else if(ls->getMaxClients() <= ls->nbClients()) {
    						int a = SERVER_FULL;
    						send(csock, (char*)&a, sizeof(int), 0);
    						shutdown(csock, 2);
    					}
    					else {
    						if(csock >= 0) {
    							FD_SET(csock, &ls->FD_Listen);
    							if(csock > ls->FD_Max) {
    								ls->FD_Max = csock;
    							}
    							printf("LoginServer: Client connection (IP: %s; Port: %d)", csin.sin_addr, csin.sin_port);
    							/*Client *c = new Client((std::string)inet_ntoa(csin.sin_addr), (int)ntohs(csin.sin_port));
    							c->Sock(csock);
    							ls->AddClient(c);
    							Packet *p = new Packet();
    							p->setInfo(1, 0);
    							c->writePacket(p);*/
    						}
    						else {
    							shutdown(csock, 2);
    						}
    					}
                    }
    				else {
    					if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
    						if (nbytes == 0) {
    							printf("LoginServer: Client disconnection (i=%d)", i);
    						}
    						else {
    							cout << "ERROR: ls_Listen --> recv()" << endl;
    						}
    						shutdown(i, 2);
    						FD_CLR(i, &ls->FD_Listen);
    					}
    					else {
    						// nbytes > 0
    						// buf à des donnees de i !
    						Packet *p;
    						p = new Packet((std::string)buf);
    						ls->PacketHandler()->Push(p);
    						send(i, buf, nbytes, 0); // renvoie des données (test ^^)
    					}
    				}
    			}
    		}
    	}
    }


    Appel du thread par LoginServer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    XServer::LoginServer::LoginServer() {
    	// [....]
    	FD_ZERO(&FD_Listen);
    	FD_ZERO(&FD_Recv);
    	if((thread = CreateThread(NULL, 0, ls_Listen, this,0, NULL)) == NULL && !stop) {
    		cout << "ERROR: LoginServer thread starting failed (ls_Listen)" << endl;
    		stop = true;
    	}
    	if(!stop) {
    		cout << "INFO: LoginServer is up and listening on port " << LS_PORT << endl;
    		// FD_stop ??
    	}
    }


    Structure de la classe LoginServer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    namespace XServer {
    	class LoginServer {
    	public:
    		fd_set		FD_Listen;
    		fd_set		FD_Recv;
    		int		    FD_Max;
    		SOCKET		sock;
    		HANDLE		thread;
    		//HANDLE		Rthread;
    		static void Start();
    		static void Shutdown();
    		bool acceptingClients();
    		int getMaxClients();
    		int nbClients();
    		Sessions* GetClients();
    		void AddClient(Client *c);
    		PacketManager *PacketHandler();
    	private:
    		bool _acceptClients;
    		int _nbClients;
    		bool running;
    		LoginServer();
    		void Loop();
    		static LoginServer *instance;
    		static Sessions* Clients;
    		static PacketManager* pm;
    	};
    };

    Donc c'est une copie avec mes trucs de: http://beej.us/guide/bgnet/examples/selectserver.c

    Voila, en plus: PacketManager est une classe qui servira de pile (Le truc semaphore que j'ai pas encore compris)
    Voici le code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    // Header
    class PacketManager {
    private:
    	vector<Packet> packets;
    	int semaphore;
    	Packet lPacket;
    public:
    	PacketManager();
    	void Push(Packet *p);
    	Packet Read();
    };
     
     
    // CPP
    PacketManager::PacketManager() {
    }
    void PacketManager::Push(Packet *p) {
    	cout << "Received packet " << p->GetPacket().c_str() << endl;
    	packets.push_back(*p);
    	++semaphore;
    }
    Packet PacketManager::Read() {
    	lPacket = packets.front();
    	packets.erase(packets.begin());
    	return lPacket;
    }



    Donc Le programme se lance parfaitement (quelques warning ici et là)
    Et voici le résultat:
    C'est quoi le problème ?



    Merci encore screetch

    nico.


    edit:
    En modifiant le cout,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout << "ERROR: ls_Listen --> select() : " << errno << endl;
    Le errno affiché est 0. (Ca peut peut etre aider ^^)
    Je file au boulot! A plus.

  10. #10
    screetch
    Invité(e)
    Par défaut
    si tu utilises winsock, le rapport d'errer se fait avec WSAGetLastError() je crois:

    http://www.sockets.com/a_c2.htm#WhereToGetErrors

  11. #11
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Hmm. Apparament l'erreur etait que mon fd_set Listen etait = null. Et en effet, au moment du check, il ny a rien dedans avant une connection.
    J'ai ajouté le socket de mon serveur dedans (comme ca en meme temps, chose que j'avais codé, je detecte en effet les connexions entrantes!)

    En meme temps, j'ai chopé un mini client qui se connecte, et ensuite send() & recv() et shutdown();
    Pour tester l'integralité de mon thread (ajout de clients, déco, et messages)

    Voici la derniere fonction (Pleins de cout, tu vas voir pourquoi)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    DWORD WINAPI ls_Listen(LPVOID para) {
    	XServer::LoginServer *ls = (XServer::LoginServer *) para;
        SOCKADDR_IN csin;
        SOCKET csock;
        int size =  sizeof(csin);
    	int i;
    	int j;
    	int errnb;
    	int nbytes;
    	char *buf;
    	cout << "thread start" << endl;
    	while(1) {
    		cout << "???" << endl;
    		ls->FD_Recv = ls->FD_Listen;
    		if ((errnb = select(ls->FD_Max+1, &ls->FD_Recv, NULL, NULL, NULL)) < 0) {
    			cout << "ERROR: ls_Listen --> select() : " << WSAGetLastError() << endl;
    			// stop thread
    		}
    		cout << "while loop" << endl;
    		for(i = 0; i <= ls->FD_Max; ++i) {
    			cout << i << " iteration of " << ls->FD_Max << endl;
    			if (FD_ISSET(i, &ls->FD_Recv)) {
    				if (i == ls->sock) {
    					cout << " iterating sock = loginserver sock" << endl;
                        csock = accept(ls->sock, (SOCKADDR *) &csin, &size);
    					if(!ls->acceptingClients()) {
    						int a = CONNECT_REFUSED;
    						send(csock, (char*)&a, sizeof(int), 0);
    						shutdown(csock, 2);
    					}
    					else if(ls->getMaxClients() <= ls->nbClients()) {
    						int a = SERVER_FULL;
    						send(csock, (char*)&a, sizeof(int), 0);
    						shutdown(csock, 2);
    					}
    					else {
    						if(csock >= 0) {
    							FD_SET(csock, &ls->FD_Listen);
    							if(csock > ls->FD_Max) {
    								ls->FD_Max = csock;
    							}
    							printf("LoginServer: Client connection (IP: %s; Port: %d)", csin.sin_addr, csin.sin_port);
    							/*Client *c = new Client((std::string)inet_ntoa(csin.sin_addr), (int)ntohs(csin.sin_port));
    							c->Sock(csock);
    							ls->AddClient(c);
    							Packet *p = new Packet();
    							p->setInfo(1, 0);
    							c->writePacket(p);*/
    						}
    						else {
    							cout << "shutdown" << endl;
    							shutdown(csock, 2);
    						}
    					}
                    }
    				else {
    					if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
    						if (nbytes == 0) {
    							printf("LoginServer: Client disconnection (i=%d)", i);
    						}
    						else {
    							cout << "ERROR: ls_Listen --> recv()" << endl;
    						}
    						shutdown(i, 2);
    						FD_CLR(i, &ls->FD_Listen);
    					}
    					else {
    						// nbytes > 0
    						// buf à des donnees de i !
    						cout << "received buf: " << buf << endl;
    						Packet *p;
    						p = new Packet((std::string)buf);
    						ls->PacketHandler()->Push(p);
    						send(i, buf, nbytes, 0); // renvoie des données (test ^^)
    					}
    				}
    			}
    		}
    	}
    }

    Donc je lance mon serveur
    Normalement je devrais obtenir (avec juste moi dans le FD_SET)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    thread start
    ???
    white loop
    0 iteration of 1
    ???
    white loop
    0 iteration of 1
    ???
    white loop
    0 iteration of 1
    ???
    white loop
    0 iteration of 1
    [...]
    Mais j'obtient juste
    ???
    En supprimant le select
    j'obtient:
    ???
    while loop


    Je suppose que mon select est mauvais, ou la boucle for ?
    merci, nico.

  12. #12
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Il m'en a fallu du temps pour résoudre cette énigme !
    Apparement la fonction select n'accepte pas la valeur 0 ou null pour timeset.
    J'ai vu sur certains forums qu'on pouvait mettre un negatif (pour infinite) mais je ne sais pas comment faire ca (si quelqu'un a la solu, je veux bien ^^)
    En plus, il fallait utiliser struct timeval !

    Donc voila la fonction corrigée, avec la boucle qui marche (yes !!)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
     
    #include "includes.h"
     
     
    DWORD WINAPI ls_Listen(LPVOID para) {
    	XServer::LoginServer *ls = (XServer::LoginServer *) para;
        SOCKADDR_IN csin;
        SOCKET csock;
        int size =  sizeof(csin);
    	int i;
    	int j;
    	int nbytes;
    	char* buf = NULL;
    	//memset(buf, NULL, sizeof(char*));
     
    	FD_ZERO(&ls->FD_Listen);
    	FD_SET(ls->sock, &ls->FD_Listen);
    	ls->FD_Max = ls->sock;
    	struct timeval tv;
    	tv.tv_sec = (time_t)0;
    	tv.tv_usec = (time_t)100;
    	for(;;) {
    		FD_ZERO(&ls->FD_tmp);
    		ls->FD_tmp = ls->FD_Listen;
    		if (select(ls->FD_Max+1, &ls->FD_tmp, NULL, NULL, &tv) < 0) {
    			cout << "ERROR: ls_Listen --> select() : " << WSAGetLastError() << endl;
    			break;
    		}
    		for(i = 0; i <= ls->FD_Max; ++i) {
    			if (FD_ISSET(i, &ls->FD_tmp)) {
    				if (i == ls->sock){ 
    					csock = accept(ls->sock, (SOCKADDR *) &csin, &size);
    					if(!ls->acceptingClients()) {
    						int a = CONNECT_REFUSED;
    						send(csock, (char*)&a, sizeof(int), 0);
    						shutdown(csock, 2);
    					}
    					else if(ls->getMaxClients() <= ls->nbClients()) {
    						int a = SERVER_FULL;
    						send(csock, (char*)&a, sizeof(int), 0);
    						shutdown(csock, 2);
    					}
    					else {
    						if(csock >= 0) {
    							FD_SET(csock, &ls->FD_Listen);
    							if(csock > ls->FD_Max) {
    								ls->FD_Max = csock;
    							}
    							printf("LoginServer: Client connection (IP: %s; Port: %d)\n", inet_ntoa(csin.sin_addr), ntohs(csin.sin_port));
    							/*Client *c = new Client((std::string)inet_ntoa(csin.sin_addr), (int)ntohs(csin.sin_port));
    							c->Sock(csock);
    							ls->AddClient(c);
    							Packet *p = new Packet();
    							p->setInfo(1, 0);
    							c->writePacket(p);*/
    						}
    						else {
    							cout << "shutdown" << endl;
    							shutdown(csock, 2);
    						}
    					}
    				}
    				else {
    					if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
    						if (nbytes == 0) {
    							printf("LoginServer: Client disconnection (i=%d)", i);
    						}
    						else {
    							cout << "ERROR: ls_Listen --> recv() - " << nbytes << "/" << WSAGetLastError() << endl;
    						}
    						shutdown(i, 2);
    						FD_CLR(i, &ls->FD_Listen);
    					}
    					else {
    						// nbytes > 0
    						// buf à des donnees de i !
    						cout << "received buf: " << buf << endl;
    						Packet *p;
    						p = new Packet((std::string)buf);
    						ls->PacketHandler()->Push(p);
    						send(i, buf, nbytes, 0); // renvoie des données (test ^^)
    					}
    				}
    			}
    		}
    	}
    	cout << "INFO: LoginServer stopped listening." << endl;
    	CloseHandle(ls->thread);
    	return 0;
    }
    Le problème maintenant, c'est que le recv ne marche pas
    (Je vais y arriver à avoir un truc qui marche hein !)

    Je recois donc un WSAGetLastError = 10053
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Software caused connection abort.
     
    An established connection was aborted by the software in your host computer, possibly due to a data transmission time-out or protocol error.
    Pourtant mon client va bien, enfin je crois (C'est un client de base récuperé sur internet ^^
    Code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <winsock2.h>
    #include <iostream>
    using namespace std;
    #pragma comment(lib, "ws2_32.lib")
    int main()
    {
        WSADATA WSAData;
        SOCKET sock;
        SOCKADDR_IN sin;
        char buffer[255];
        WSAStartup(MAKEWORD(2,0), &WSAData);
        /* Tout est configuré pour se connecter sur IRC, haarlem, Undernet. */
        sock = socket(AF_INET, SOCK_STREAM, 0);
        sin.sin_addr.s_addr = inet_addr("127.0.0.1");
        sin.sin_family = AF_INET;
        sin.sin_port = htons(8484);
        int c = connect(sock, (SOCKADDR *)&sin, sizeof(sin));
    	//std::cout << "connect result : " << WSAGetLastError() << std::endl;
    	char *Z = "Hello";
    	send(sock, Z, strlen(Z), 0); 
        //recv(sock, buffer, sizeof(buffer), 0);
    	//send(sock, (const char*)Z, strlen(Z), 0); 
    	while(1) { Sleep(1); }
    	//closesocket(sock);
        //WSACleanup();
        return 0;
    }

  13. #13
    Membre très actif
    Homme Profil pro
    Second de cuisine
    Inscrit en
    Avril 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Second de cuisine
    Secteur : Alimentation

    Informations forums :
    Inscription : Avril 2005
    Messages : 193
    Par défaut
    Je vais poster ci-après une résolution pour le problème de buffer qui merde.


    Si votre buffer recoit plus que ce qu'il devrait recevoir (Pourquoi, je ne sais point !): Exemple

    Votre client envoie "salut\n" dans une boucle while.
    Votre server recevra "salut\n", si jamais il recoit plus, vous pouvez utiliser ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char **packets = split(buffer, '\n'); // pointeur vers tableau de caractere
    while(*packets && strlen(*packets) > 0) { // je parcoure le tableau
    								char *packet = *packets; // je recup' une entree;
    // voila ^^ utilisez packet :)
    								*packets++; // je passe a la suivante
    							}

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 5
    Dernier message: 21/04/2011, 11h54
  2. problème sur socket
    Par chuko dans le forum Réseau
    Réponses: 7
    Dernier message: 06/09/2008, 20h19
  3. [CR8] Problème tableau sur plusieurs pages???
    Par christophe28 dans le forum SAP Crystal Reports
    Réponses: 5
    Dernier message: 02/11/2004, 15h46
  4. [MFC] Problème pointeur sur une classe
    Par mick74 dans le forum MFC
    Réponses: 7
    Dernier message: 14/04/2004, 14h17
  5. Notion sur Socket UDP
    Par oxor3 dans le forum Développement
    Réponses: 3
    Dernier message: 05/04/2004, 00h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo