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

Langage C++ Discussion :

Template, et operateur surchargés !


Sujet :

Langage C++

  1. #1
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut Template, et operateur surchargés !
    Bonjour !

    Alors j'ai 2 petits problèmes, le premier, au niveau de l'écriture d'un template:

    Avant j'avais dans ma classe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	int GetHeader();
    	int GetAction();
    	int ReadInt();
    	long ReadLong();
    	std::string ReadString();
    Et en passant sur le chapitre templates de mon bouquin, je me suis dis que je devais changer ca !

    Ma classe:
    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
     
    class Packet {
    private:
    	int index;
    	queue<byte> *bytes;
    public:
    	Packet();
    	Packet(char &s);
    	~Packet();
    	template<typename T>void read(T &buf);
    	void Next();
    	Packet& operator+(long l);
    	Packet& operator+(int i);
    	Packet& operator+(char *s);
    	Packet& operator+(byte b);
     };
     
    int substr(char *s, int start, int length);
    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
     
    Packet::~Packet() {
    	//delete this;
    }
    Packet::Packet() {
    }
    Packet::Packet(char &s) {
    	bytes = new queue<byte>;
    	int len = strlen(&s);
    	cout << &s << endl;
    	int _bytes = floor((float)len/2)-floor((float)len/6);
    	for(int i=1; i<=_bytes; ++i) {
    		bytes->push(substr(&s, (i-1)*3, 1));
    	}
    }
    void Packet::Next() {
    	++index;
    }
    template<typename T>void Packet::read(T &buf) {
    	cout << "size of argument: " << sizeof(T) << endl;
    }
    Packet& Packet::operator+(long l) {
    	return *this;
    }
    Packet& Packet::operator+(int i) {
    	return *this;
    }
    Packet& Packet::operator+(char *s) {
    	for(int i = 0; i < strlen(s); ++i) {
    		bytes->push(s[i]);
    	}
    	return *this;
    }
    Packet& Packet::operator+(byte b) {
    	bytes->push(b);
    	return *this;
    }
    int substr(char *s, int start, int length) {
    	char t[2] = {s[start], s[(start+length)]};
    	return (int)strtol(t, NULL, 16);
    }
    J'aimerais pouvoir faire par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    	char *p = "01 02 05 6b 6b 6b 6b 6b ff aa";
    	Packet *pk = new Packet(*p);
    	int h, a, l;
    	long v;
    	char *str;
    	pk->read(h); // h = 01 - readint
    	pk->read(a); // a = 02 - readint
    	pk->read(str); // str = 6b 6b 6b 6b 6b  (en prenant en compte de lire le 05 = length du str)
    	pk->read(v); // v = aa ff
    Voila,
    le deuxième problème est au niveau de la surcharge d'operateurs

    J'ai un thread
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    DWORD WINAPI ls_Listen(LPVOID para) {
    	XServer::LoginServer *ls = (XServer::LoginServer *) para;
    //[.......]
    Plus bas, j'accepte un socket et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    							Client *c = new Client((std::string)inet_ntoa(csin.sin_addr), (int)ntohs(csin.sin_port), csock);
    							ls += c;
    Donc je creer mon object client, en donnant toutes les valeurs (tout va bien)
    VC2010 me dit que 'c' doit avoir un type integral ou enum ?!

    La déclaration de LoginServer operator+:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    XServer::LoginServer& XServer::LoginServer::operator+(Client &c) {
    	Clients += c;
    	return *this;
    }

    La déclaration de Clients operator+:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Sessions& Sessions::operator+(Client &c) {
    	clients[c.Get()->_aid] = &c;
    	return *this;
    }
    Comment faire pour corriger cette erreur?

    Merci d'avance, nico

  2. #2
    Membre confirmé Avatar de Kazh Du
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2011
    Messages
    152
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2011
    Messages : 152
    Points : 561
    Points
    561
    Par défaut
    Je ne comprends pas ton premier problème, qu'est-ce qui ne fonctionne pas ? (je n'ai pas de compilateur c++ avec moi, je ne peux pas tester)

    pour ton 2eme problème :
    lorsque tu fais "ls += c;", tu additionnes 2 pointeurs ! La syntaxe correcte serait :
    Ensuite, l'opérateur += n'a pas été défini.
    Enfin, je dois avouer que je ne comprends pas pourquoi tu utilises l'opérateur +, à mes yeux ça ne veut pas dire grand chose. Mieux vaut utiliser une méthode addClient(Client &c) qui a plus de sens. Surtout que la syntaxe correcte d'un opérateur + devrait ressembler à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    class1& class1::operator+(const class2 &c) const;
    car elle ne devrait modifier aucun paramètre mais créer un nouvel objet.

  3. #3
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut
    Bojour,

    Pour mon Premier probleme, jessayer de simplifier toutes mess methodes readint readlong readstring en une seule methode, et je crois quil faut les templates pour ca!

    Pour le deuxieme, je voulais simplement ne pas utiliser addclient, deleteclient mais les operateur + et -!

  4. #4
    Membre confirmé Avatar de Kazh Du
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2011
    Messages
    152
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2011
    Messages : 152
    Points : 561
    Points
    561
    Par défaut
    1er pb :
    ta fonction ne fait pas ce que tu veux ?
    que veux tu quelle fasse précisément ? (afficher le contenu de ta variable ?)
    que fait elle actuellement ? (afficher la taille de de ta variable ? ou rien du tout = beau plantage)

    2eme pb :
    Si tu veux vraiment les utiliser (libre à toi) il est préférable de définir l'opérateur += (et -=) plutôt que + et - car ces derniers ne devraientpas modifier les objets, et SURTOUT c'est eux que tu as utilisé dans ta fonction. + et += ne sont pas équivalents. Et surtout veille à bien faire la distinction entre pointeur et référence, ce que tu n'avais pas fais (d'où ton erreur).

    PS : Je comprend qu'il est tentant de vouloir les utiliser, mais dans ton cas ça ne se justifie pas, il faut penser en terme de lisibilité et de compréhension du code. Pense à des listes : tu peux ajouter un objet à ta liste avec add(objet) et tu pourrais vouloir créer la fusion de 2 listes avec un +.

    PS 2 : dans le cas d'un pointeur, l'additionner avec un entier (ce que ton compilateur s'attendait à faire visiblement : un pointeur peut parfois être traité comme un entier) reviens à le faire avancer d'autant d'octets, c'est ce qu'il se passe avec un tableau (avec un sizeof en plus ).

  5. #5
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut
    Premier probleme:
    En gros, j'ai dans ma classe ceci:
    Quand je fais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *p = "01 02 05 6b 6b 6b 6b 6b ff aa";
    Packet *pk = new Packet(*p);
    Mon constructeur va remplir bytes avec (dans ce cas)
    1,2,5,107,107,107,107,107,255,170

    je veux, pouvoir faire
    int a = pk->read();
    int a = pk->read<int>();
    int a = pk->read(<int>);
    int a;
    pk->read(&a);

    et, que je puisse egalement faire
    long L = pk->read();
    char c[a] = pk->read();

    Et au final, la fonction read, prendra la taille de ce que j'aimerais lire (int/char = 1byte, long = 2 bytes, "string" = taille du char[])

    Pour le deuxieme probleme,
    J'ai simplement vu que l'on pouvait simplifier un "add()" ou delete() avec les operateurs en les surchargeant.
    Donc ma question est plutot, niveau rapidité, operateur surchargés ou fonctions ?

    merci,
    nico

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    Déjà, je me demande pourquoi utiliser un pointeur sur queue<byte> (je présume que c'est en fait une std::queue !)

    Tu te compliques la vie inutilement en
    • devant gérer par toi meme la mémoire allouée à ce pointeur
    • rendant la copie de ton objet beaucoup moins sécurisante (tu n'as pas défini le constructeur par copie et l'opérateur d'affectation, ce qui fait que tu vas etre confronté à de sérieux problèmes de double tentatives de libération de la mémoire)
    • devant manipuler le membre (la queue) au travers s'une syntaxe différente
    • violant le principe de la responsabilité unique, parce que tu rajoute à ta classe, dont la responsabilité devrait n'être que de gérer des bytes, le fait de devoir gérer également... l'élément qui permet de les maintenir en mémoire
    • ... j'oublie peut etre une ou l'autre chose

    J'en profite d'ailleurs pour faire la meme remarque pour l'utilisation de ta classe : pourquoi utiliser un pointeur, avec tous les problèmes que cela sous entend, alors qu'il serait "si facile" d'utiliser un objet tout à fait classique

    Ensuite, je suis d'accord avec Kazh Du : les opérateur + et += servent par convention pour des additions (au sens mathématique), voir pour des concaténations (cf std::string) ...

    Or, ce que tu fais ici, c'est rajouter "quelque chose" à un paquet...

    Tu détourne donc l'opérateur de l'utilisation que "l'imaginaire collectif" envisage pour lui, et ce n'est vraiment pas, mais alors vraiment pas une bonne idée.

    Si tu devais définir un opérateur + (ou +=) tu devrais le définir de sorte à... additionner deux objets de type "Packet", pour autant que cela ait un sens!

    Enfin, les templates sont un monde magnifique, mais ils doivent venir "en supplément" du paradigme OO...

    Il est utile de les utiliser s'il y a vraiment un intérêt particulier à le faire (pour éviter la copie de code, par exemple), mais, vouloir utiliser les template "pour le plaisir de les utiliser" n'a finalement pas beaucoup de sens si leur utilisation ne fait que "te compliquer la vie" !

    L'idée des tempalte, c'est de se dire que "si je ne sais pas exactement quel type de donnée j'aurai à manipuler, je sais néanmoins parfaitement comment je dois les manipuler".

    La question à se poser est donc "est il possible d'imaginer une logique quelconque qui me permettra de manipuler aussi bien des int, des long, des double, des byte ou des float (en somme : n'importe quel type de donnée compatible ) de sorte à pouvoir les insérer de manière cohérente dans la queue de byte " et, surtout "est-ce que cela me facilitera réellement la tache "

    J'ai envie de dire que, maintenant que les opérateurs sont définis pour l'ensemble des types susceptibles d'être utilisés, peut etre te fais tu simplement du mal pour rien

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par koala01 Voir le message
    [*]devant manipuler le membre (la queue)
    Hum...

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par oodini Voir le message
    Hum...
    oui, j'avoue, cet argument n'est pas des meilleur, mais tu laisse un arbre cacher la forêt...

    De prime abord, je trouve déjà purement et simplement aberrant de vouloir utiliser un pointeur sur une collection issue de la STL parce que:
    • ces collections ne sont pas faites pour être héritée publiquement, ce qui supprimes dés le départ un grand nombre de cas d'utilisation plaidant en faveur de l'utilisation de pointeur.
    • tu rajoute une indirection supplémentaire et inutile : les collections de la STL gèrent parfaitement bien la mémoire nécessaire au maintient des éléments qu'elles contiennent.
    • Si tu dois passer une collection en paramètre sans en provoquer la copie, autant la passer par référence, éventuellement constante, voire, c'est souvent mieux, passer carrément un "range" d'itérateurs sur les éléments que tu souhaites utiliser.
    En outre, l'utilisation de pointeurs tend à provoquer le fait que l'on se pose la question de qui le crée, qui l'utilise et qui le détruit...

    Pour son malheur, le PO ne s'est pas posé cette question, et il en résulte que les constructeur par copie et opérateur d'affectation vont se contenter de copier l'adresse dans le pointeur, avec des résultats catastrophiques de tentative de double libération de la mémoire, ou d'accès à une adresse mémoire déjà libérée

    Enfin, il n'aura pas le problème, au vu du code, parce qu'il n'a pas pensé à invoquer delete dans le constructeur... à défaut, il aura des fuites mémoires!!!

    J'espère pour lui que son application ne doit pas tourner en tant que service acessible 24h/7j (et il ne faudra pas aller jusque là pour que le PO commence à éprouver des problèmes )

    On le sait, la notion "d'adresse à laquelle se trouve une variable" est sans doute la notion qui a permis le plus grand nombre d'évolutions et de nouveaux langages (même si certains langages "cachent" cette notion).

    Mais on sait aussi que cette même notion, si elle est mal comprise ou mal utilisée est, aussi, la notion qui est la cause du plus grand nombre de bugs, tous langages et toutes applications confondues.

    C++ permet de restreindre l'utilisation de cette notion à une poignée de cas clairement identifiés (lorsqu'il est nécessaire de créer un objet dont on défini le type de manière dynamique)... Pourquoi diable vouloir réintroduire cette notion à tout bout de champs là où elle n'est pas indispensable (voire pire, là où elle est inutile ) s'il y a moyen de s'en passer

  9. #9
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut
    Pour le read:
    Je suis retourné vers mes anciennes fonctions ^^ template = un jour peut etre (Quan je comprendrais comment ca marche xD)

    Ensuite:
    J'en profite d'ailleurs pour faire la meme remarque pour l'utilisation de ta classe : pourquoi utiliser un pointeur, avec tous les problèmes que cela sous entend, alors qu'il serait "si facile" d'utiliser un objet tout à fait classique
    Tu parles de ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Packet *pk = new Packet([...]);
    Quand je recois un packet sur mon serveur, il fera ce chemin.

    Thread: Reception
    Je recv() dans un buffer, je transforme en packet, j'envoie la variable à la classe PacketManager, qui en fonction du premier byte lu, va renvoyer ce packet à un worker thread.
    Puis-je utiliser un Packet() sans pointeur alors?

    Petit problème au passage, voici la réception de mes packets:
    EDIT:
    En fait, j'ai un petit soucis avec mes packets.
    Voici la réception de mes packets:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // Packet *packet; int nbytes, char buf[500];
    					nbytes = recv(i, buf, sizeof(buf), 0);
    					if(nbytes > 0) {
    						buf[nbytes] = 0;
    						packet = new Packet(*buf);
    						cout << "buf=" << buf << " (" << nbytes << ")" << endl;
    						ls->PacketHandler()->Push(packet);
    					}
    En prenant en compte ce client (tout à fait réel, envoie de 3 packets simultanés, cela aurait pu etre 100)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    		char *forlogin = "01 00 01 00\r\n";
    		send(sock, forlogin, strlen(forlogin), 0); 
    		char *forchnls = "FF 00 00 00\r\n";
    		send(sock, forchnls, strlen(forchnls), 0); 
    		char *forworld = "0F 00 00 00\r\n";
    		send(sock, forworld, strlen(forworld), 0);
    Je vois sur la console:
    buf=01 00 01 00 (13)
    MAIS AUSSI:
    buf=01 00 01 00
    FF 00 00 00 (26)
    OU ENCORE:
    buf=01 00 01 00
    FF 00 00 00
    0F 00 00 00 (39)

    Une fois ceci résolu, je pourrais enfin voir si mon PacketManager envoie les Packets au bon Worker Thread !

    merci, nico!

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par DakM Voir le message
    Ensuite:
    J'en profite d'ailleurs pour faire la meme remarque pour l'utilisation de ta classe : pourquoi utiliser un pointeur, avec tous les problèmes que cela sous entend, alors qu'il serait "si facile" d'utiliser un objet tout à fait classique
    Tu parles de ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Packet *pk = new Packet([...]);
    oui, tout à fait...
    Quand je recois un packet sur mon serveur, il fera ce chemin.

    Thread: Reception
    Je recv() dans un buffer, je transforme en packet, j'envoie la variable à la classe PacketManager, qui en fonction du premier byte lu, va renvoyer ce packet à un worker thread.
    Puis-je utiliser un Packet() sans pointeur alors?
    oui, bien sûr...

    Passe ton paquet par valeur, ou, mieux encore, par référence (constante si ton paquet ne doit pas être modifié), si tu veux en éviter la copie!!!
    Petit problème au passage, voici la réception de mes packets:
    EDIT:
    En fait, j'ai un petit soucis avec mes packets.
    Voici la réception de mes packets:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // Packet *packet; int nbytes, char buf[500];
    					nbytes = recv(i, buf, sizeof(buf), 0);
    					if(nbytes > 0) {
    						buf[nbytes] = 0;
    						packet = new Packet(*buf);
    						cout << "buf=" << buf << " (" << nbytes << ")" << endl;
    						ls->PacketHandler()->Push(packet);
    					}
    que penses tu de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int nbyte;
    char buf[500];
    nbytes = recv(i, buf, sizeof(buf),0);
        if(nbytes > 0) 
        {
            buf[nbytes] = 0;
            Packet packet(buf);
            cout << "buf=" << buf << " (" << nbytes << ")" << endl;
    	ls->PacketHandler()->Push(packet);
        }
    en modifiant la fonction Push sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void PacketHander::Push(Packet const & toPush)
    (je me suis limité à la modification de la mise en page et à l'utilisation d'un objet normal et non d'un pointeur )
    En prenant en compte ce client (tout à fait réel, envoie de 3 packets simultanés, cela aurait pu etre 100)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    		char *forlogin = "01 00 01 00\r\n";
    		send(sock, forlogin, strlen(forlogin), 0); 
    		char *forchnls = "FF 00 00 00\r\n";
    		send(sock, forchnls, strlen(forchnls), 0); 
    		char *forworld = "0F 00 00 00\r\n";
    		send(sock, forworld, strlen(forworld), 0);
    cela n'a rien à voir...

    Le client et le serveur doivent travailler de manière tout à fait indépendante : après tout, le client doit pouvoir travailler avec un autre serveur, et inversement

    Ce qui importe, c'est que ton client et ton serveur émettent / recoivent des données selon un protocole donné, et qu'ils doivent donc le respecter

  11. #11
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut
    Rebonsoir!
    Merci pour ta réponse, mais j'ai lancé un test, avec une dixaine de clients qui envoyaient un packet / 10ms pour voir le résultat.



    CATASTROPHE.




    En gros, si mon serveur recois un message toutes les environ~ 10ms, tout va bien.
    Le buffer prend en charge " un packet ".
    Avec dix clients, le buffer (cf: image) se transforme en woodstock.
    Je vais poster l'integralité des fonctions, en esperant ne pas me faire assassiner (Il y a probablement pleins de failles ^^)

    Le client utilisé:
    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
    #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[125];
        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;
    	int N = 0;
    	//send(sock, (const char*)Z, strlen(Z), 0); 
    	while(1) {
    		char *forlogin = "01 00 01 00\r\n";
    		send(sock, forlogin, strlen(forlogin), 0); 
    		Sleep(10);
    		char *forchnls = "FF 00 00 00\r\n";
    		send(sock, forchnls, strlen(forchnls), 0); 
    		Sleep(10);
    		char *forworld = "0F 00 00 00\r\n";
    		send(sock, forworld, strlen(forworld), 0); 
    		/*
    		if((N =recv(sock, buffer, sizeof(buffer), 0)) > 0) {
    			buffer[N] = 0;
    			cout << "buffer : " << buffer << endl;
    		}*/
    		Sleep(10);
    	}
    	//closesocket(sock);
        //WSACleanup();
        return 0;
    }
    Les deux threads
    • Le premier: Ecoute les connections, et envoie les packet à PacketManager !
    • Le deuxieme: Regarde si il y a un packet à traiter, et le traite.


    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    #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 = sizeof(int);
    	int j = sizeof(int);
    	int nbytes;
    	char buf[PACKET_SIZE];
    	Packet packet;
     
    	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), csock);
    							/*//ls += c;
    							Packet *p = new Packet();
    							//p->setInfo(1, 0);
    							c->writePacket(p);
    							char buf[128];
    							sprintf_s(buf, "hey client %d", csock);
    							send(csock, buf, sizeof(buf), 0);*/
    						}
    						else {
    							cout << "shutdown" << endl;
    							shutdown(csock, 2);
    						}
    					}
    				}
    				else {
    					nbytes = recv(i, buf, sizeof(buf), 0);
    					if(nbytes > 0) {
    						buf[nbytes] = 0;
    						packet = Packet::Packet(*buf);
    						cout << "buf=" << buf << " (" << nbytes << ")" << endl;
    						ls->PacketHandler()->Push(packet);
    					}
    					else { //(nbytes <= 0)
    						printf("LoginServer: Client disconnection (i=%d) (%d)\n", i, WSAGetLastError());
    						shutdown(i, 2);
    						FD_CLR(i, &ls->FD_Listen);
    					}
    				}
    			}
    		}
    	}
    	cout << "INFO: LoginServer stopped listening." << endl;
    	CloseHandle(ls->thread);
    	return 0;
    }
     
    DWORD WINAPI pm_Parse(LPVOID packetManager) {
    	PacketManager *pm = (PacketManager *)packetManager;
    	Packet packet;
    	byte header;
    	for(;;) {
    		if(!pm->TODO()) {
    			cout << "INFO: No packets to parse, sleeping now 3 seconds." << endl;
    			Sleep(3000);
    			continue;
    		}
    		packet = pm->Read();
    		header = packet.read();
    		if(header > 0 && header <= 10) {
    			cout << "Packet is going to login" << endl;
    			// login -> ingame
    		}
    		else if(header <= 20) {
    			cout << "Packet is going to world" << endl;
    			// world
    		}
    		else {
    			cout << "Packet is going to channel" << endl;
    			// channel
    		}
    	}
    	return 0;
    }

    Enfin, voici le code de PacketManager, la passerelle entre les deux thread. (Attention, c'est un peu un assemblage de ce que j'ai trouvé sur MSDN, sans trop savoir si ca allait marcher !)
    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
    class PacketManager {
    private:
    	queue<Packet> packets;
    	int semaphore;
    	Packet lPacket;
    	HANDLE Semaphore, Mutex;
    public:
    	PacketManager(bool *success);
    	void Push(Packet &p);
    	Packet Read();
    	bool TODO();
    };
    #include "includes.h"
     
     
    PacketManager::PacketManager(bool *success) {
    	Semaphore = CreateSemaphore(NULL, 0, NULL, (LPCWSTR)"PM_SEMAPHORE");
    	if((Mutex = CreateMutex(NULL, false, (LPCWSTR)"PM_MUTEX")) != NULL) {
    		cout << "INFO: PacketManager is now ready" << endl;
    		*success = true;
    	}
    	else {
    		cout << "ERROR: PacketManager could not start up (" << GetLastError() << ")" << endl;
    		success = false;
    	}
    }
    bool PacketManager::TODO() {
    	return packets.empty();
    }
    void PacketManager::Push(Packet &p) {
    	WaitForSingleObject(Semaphore,INFINITE);
    	WaitForSingleObject(Mutex,INFINITE);
    	packets.push(p);
    	ReleaseMutex(Mutex);
    	ReleaseSemaphore(Semaphore,1,NULL);
    }
    Packet PacketManager::Read() {
    	WaitForSingleObject(Semaphore,INFINITE);
    	WaitForSingleObject(Mutex,INFINITE);
    	lPacket = packets.front();
    	packets.pop();
    	ReleaseMutex(Mutex);
    	return lPacket;
    }
    Le but du test avec les packets bien spécifiques du client, est qu'ils commencent par 01, 0F et FF. Pour avoir les 3 cout de mon deuxieme thread.
    A la place des trois cout, le thread va renvoyer par chronoposte le packet à un des trois autres futurs thread qui se chargeront d'analyser le reste du packet, et de renvoyer la(les) réponse(s) au(x) (autres/TOUS les) client(s) !
    nb: Quand n'importe quel client enverra un packet, le serveur doit le lire comme tel, et pas rajouter dans le buffer les autres packets, sinon c'est un peu débil de sa part non ?

    J'accepte toutes les critiques, mais par pitié, n'utilisez pas des mots trop pro, car j'aurais trop de mal à comprendre :V

    Merci d'avance à ceux qui auront le courage de trouver la solution à ce problème ^^

    nico

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Une petite question supplémentaire...

    Le code que tu donnes est, très clairement, non portable car il manipule des éléments propres à window (#include <winsock2.h>, #pragma comment(lib, "ws2_32.lib"))

    Pourquoi n'essayerais-tu pas de passer à un mode qui serait beaucoup plus dans l'esprit de C++ en décidant d'utiliser la bibliothèque boost asio, étant donné que tu ne cherche visiblement qu'à utiliser un protocole TCP par internet

    Cela aurait quelques avantages, dont:
    • celui de rendre le code beaucoup plus portable : il "suffit" de gérer les dépendances envers les différentes bibliothèques (mswsock, ws2_32, boost_system, boost_regex, sous windows, vérifier par quoi remplacer mswsock et ws2_32 wouw linux) au niveau du projet pour que ton serveur puisse travailler aussi bien sous linux que sous window
    • une grande partie du travail (la partie "commune" à tous les serveurs et à tous le client) est déjà faite: qu'il te suffise de voir les exemples pour t'en convaincre
    • j'en oublie, et de meilleurs
    Bon, après, tu me diras sans doute que
    oui, mais devoir installer boost pour disposer de asio, alors que je peux m'en sortir avec le bibliothèques de windows, ca fait un peut utiliser un bazooka pour tuer une mouche
    mais tu te rendras compte à l'usage que boost fournit vraiment pas mal de choses, qui pourront d'ailleurs peut etre s'avérer intéressantes pour la suite de ton projet (une gestion de thread portable, la sérialisation, la création d'archives, les tests unitaires, et j'en passe)!

    Tu n'es même pas obligé de refaire le client si tu ne le souhaites pas, mais, étant donné que tu éprouves des problèmes avec la partie serveur et que boost peut vraiment te faciliter la vie pour ce point particulier, il serait dommage de s'en priver

  13. #13
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut
    Salut,

    Je sais pas si j'ai raison, mais je pense que étant un bon noob en C+++ je préfère d'une part,
    ne pas porter mon code vers linux (Un jour, j'installerais linux sur mon ordi, et je ferais une modification !
    ne pas utiliser d'énormes librairies type boost. (Surtout que je vais utiliser à peine 1% de ce quelle offre.

    Apres, 'ai toujours ce probleme de buffer, aurais tu une idée pour résoudre ce problème ?

    nico

  14. #14
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par DakM Voir le message
    Salut,

    Je sais pas si j'ai raison, mais je pense que étant un bon noob en C+++ je préfère d'une part,
    ne pas porter mon code vers linux (Un jour, j'installerais linux sur mon ordi, et je ferais une modification !
    ne pas utiliser d'énormes librairies type boost. (Surtout que je vais utiliser à peine 1% de ce quelle offre.
    Honnetement, je ne crois pas que tu aies raison...

    Bien au contraire, c'est justement parce que tu es noob qu'il est largement préférable de prendre tout de suite les bonnes habitudes

    La STL (la bibliothèque fournie par le standard) et boost sont deux incontournables, la première parce qu'elle te permet d'éviter un tas d'écueils comme ceux liés à la gestion dynamique de la mémoire et d'écrire un code réellement C++, la deuxième parce que c'est un complément des plus utiles à la première ( la nouvelle norme, et, avant cela le TR1 a d'ailleurs intégré une partie de boost... c'est te dire )

    Plus tu t'intéressera rapidement aux possibilités qu'offrent ces deux bibliothèques ( meme si boost est en réalité une collection de bibliothèques ) plus tu sera en mesure d'évoluer rapidement, meme si je peux comprendre que cela puisse sembler inutile au début
    Apres, 'ai toujours ce probleme de buffer, aurais tu une idée pour résoudre ce problème ?

    nico
    Es tu conscient du fait que tu n'envoie jamais que le premier caractère de ton buffer à ta variable Packet (en envoyant "ce qui est pointé par buf" au constructeur de Packet qui, soit dit en passant, prend une référence sur un seul caractère)...

    Le problème vient peut etre de là, pourquoi ne pas envoyer directement l'ensemble du buffer

  15. #15
    Membre régulier
    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
    Points : 99
    Points
    99
    Par défaut
    Bon je vais faire un include boost (je l'ai déjà sur mon ordi ^^)


    Es tu conscient du fait que tu n'envoie jamais que le premier caractère de ton buffer à ta variable Packet (en envoyant "ce qui est pointé par buf" au constructeur de Packet qui, soit dit en passant, prend une référence sur un seul caractère)...

    Le problème vient peut etre de là, pourquoi ne pas envoyer directement l'ensemble du buffer
    Je sais pas faire ca.


    Edit:
    Petit malin que je suis, j'ai changé un bout de mon coe en me disant, allez, mon buffer va marcher maintenant. Eh beh non.
    En spammant mon serveur, avec 3 packets simultanés (mais 3 send() différents), mon buffer est une fois composé de 1 packet, une fois de 2, une fois de 1, 3, 2 ... Chiant !

    Aussi, j'ai testé mon thread qui parse les packets, et ca fonctionne pas !
    C'est le thread qui fait le lien entre la reception du packet (thread=ls_Listen) et l'analyse du premier byte (thread=pm_Parse).

    Voici le code de PacketManager & pm_Parse !
    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
    PacketManager::PacketManager(bool *success) {
    	//Semaphore = CreateSemaphore(NULL, 0, NULL, (LPCWSTR)"PM_SEMAPHORE");
    	if((Mutex = CreateMutex(NULL, false, (LPCWSTR)"PM_MUTEX")) != NULL) {
    		cout << "INFO: PacketManager is now ready" << endl;
    		*success = true;
    	}
    	else {
    		cout << "ERROR: PacketManager could not start up (" << GetLastError() << ")" << endl;
    		success = false;
    	}
    }
    bool PacketManager::TODO() {
    	return packets.empty();
    }
    void PacketManager::Push(SOCKET c, XServer::Packet p) {
    	//WaitForSingleObject(Semaphore,INFINITE);
    	WaitForSingleObject(Mutex,INFINITE);
    	XServer::PacketInformation pi;
    	pi.client = c;
    	pi.packet = p;
    	packets.push(pi);
    	ReleaseMutex(Mutex);
    	//ReleaseSemaphore(Semaphore,1,NULL);
    }
    XServer::PacketInformation PacketManager::Read() {
    	//WaitForSingleObject(Semaphore,INFINITE);
    	WaitForSingleObject(Mutex,INFINITE);
    	lPacket = packets.front();
    	packets.pop();
    	ReleaseMutex(Mutex);
    	return lPacket;
    }
    //------------------------------------------
    DWORD WINAPI pm_Parse(LPVOID packetManager) {
    	PacketManager *pm = (PacketManager *)packetManager;
    	XServer::PacketInformation pi;
    	PacketFactory Factory;
    	SOCKET client;
    	byte header;
    	for(;;) {
    		if(!pm->TODO()) {
    			cout << "INFO: No packets to parse, sleeping now 3 seconds." << endl;
    			Sleep(3000);
    			continue;
    		}
    		else {
    			cout << "found one packet" << endl;
    		}
    		pi = pm->Read();
    		Factory = PacketFactory(pi.packet);
    		client = pi.client;
    		header = Factory.read();
    		if(header > 0 && header <= 10) {
    			cout << "packet 1" << endl;
    			//send(client, "packet 1\n", 9, 0);
    		}
    		else if(header <= 20) {
    			cout << "packet 2" << endl;
    			//send(client, "packet 2\n", 9, 0);
    		}
    		else {
    			cout << "packet 3" << endl;
    			//send(client, "packet 3\n", 9, 0);
    		}
    	}
    	return 0;
    }

    Je pars au boulot, en ésperant que d'ici 8 heures, un membre dévoué m'aide à résoudre mon problème !

    nico !

Discussions similaires

  1. Réponses: 22
    Dernier message: 17/07/2008, 09h51
  2. souci classe template et surcharge operateur
    Par loicounet dans le forum Langage
    Réponses: 8
    Dernier message: 28/04/2008, 15h01
  3. Optimisation Listechainé avec template et operateur surchargé.
    Par Alain Defrance dans le forum Langage
    Réponses: 8
    Dernier message: 29/12/2007, 18h25
  4. Réponses: 6
    Dernier message: 12/07/2007, 18h18
  5. [Template] Surcharge operateur
    Par juls64 dans le forum C++
    Réponses: 7
    Dernier message: 04/05/2007, 19h35

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