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

Développement Discussion :

Concevoir une application reseau ( serveur/client )


Sujet :

Développement

  1. #1
    Membre éclairé
    Avatar de ZouBi
    Inscrit en
    Octobre 2007
    Messages
    508
    Détails du profil
    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 508
    Points : 812
    Points
    812
    Par défaut Concevoir une application reseau ( serveur/client )
    Bonjour,
    Voilà, je souhaite concevoir une mini application ( serveur et client deux programmes distincts ), où chaque client pourrai bouger un personnage sur l'écran en 2D, et il verrait aussi les autres personnages des autres clients bouger.

    Je l'ai commencé et ça marche, une fois sur deux ( erreur de segmentation, et j'en passe ).
    Donc je cherche juste quelqu'un pouvant m'aider pour les bases d'un programme réseau ( juste l'algorithmie, manière de coder... la syntaxe je connais déjà ).

    Pour la librairie, j'utilise SDL_net.

    Bon voilà comment j'ai procédé :
    Serveur :
    le programme principale tourne en boucle, attendant la connexion de client.
    Lorsqu'un client se connecte, on l'ajoute dans un vecteur de type Client.
    La classe Client contient donc le socket, l'id du client, et un attribut Personnage ( coordonnée, sprite... ).

    Le serveur possède deux threads.
    - Un pour l'envoi des informations aux clients :
    Il tourne en boucle, en parcourant le vecteur Client, et à chaque fois en leur envoyant tous les personnages de chaque client connecté.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SDLNet_TCP_Send( liste_clients[i].getSocket(), &( liste_clients[j].getPerso() ), sizeof( Personnage ) );
    - Un autre thread pour la reception des données
    Une boucle parcourant le vecteur Client pour récuperer le type Personnage de chaque socket des clients. Et mise à jour du personnage dans le tableau.

    Coté serveur, ça va.

    Maintenant coté client :
    Le moteur du jeu, on voit notre bonhomme de notre jeu et tout bouger.

    2 variables globales : Notre Perso, et un vecteur Perso pour les autres clients.

    Et pareil que le serveur, deux threads :
    Un où on emet sans cesse notre perso au serveur.
    Un autre où on recoit et construit notre vecteur des Persos des autres clients.

    J'utilise aussi des mutexs, comme par exemple, quand on parcoure la liste des Personnages pour les afficher, faire en sorte de bloquer le thread s'occupant de la reception pour pas en même temps écrire dans le vecteur en même temps qu'on le lit...

    Voilà mais bon, je sais que c'est pas parfait, et je n'adopte pas la bonne manière de coder pour ce genre d'application, c'est pour ça que je demande l'aide ou autres pour pouvoir m'inspirer...

  2. #2
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    /!\ pavé following !

    Salut,

    Je te propose une idée d'architecture pour du client/Serveur relativement générique. Je ne dis (surtout) pas que c'est le Saint Graal, mais tu trouveras peut-être quelques idées pour t'inspirer, et tant qu'à écrire un pavé, ça re-servira peut-être à d'autres personnes.

    Trois idées principales :

    - ces applications seront essentiellement 'event driven'. Ainsi, du côté du serveur, tu vas probablement n'avoir à faire des traitements que lorsqu'un client produit un événement, genre: "un nouveau client se connecte", "un client se déconnecte", "des données sont reçues d'un client". Le reste du temps le serveur va probablement 'dormir' gentillement.

    - Pour la généricité, un design pattern est très intéressant pour ce genre de besoin: le pattern 'Observer' . En deux mots, il te permet de définir pour un objet (appelons-le EMETTEUR) une collection d'événements qu'il sera amené à 'émettre'. N'importe quel autre objet qui implémente les bons récepteurs peut alors s'enregistrer auprès d'EMETTEUR pour être prévenu lorsqu'un de ses événement est émis.
    Ce design pattern te permettra de gagner beaucoup en généricité et en modularité, et te fournira les bases pour gérer l'événementiel. Je te laisse te renseigner sur ce pattern. Wikipédia, google et (certainement) des tutos de developpez.com sont tes amis.

    - pour les données proprement dites: il faut absolument définir le format des données échangées, et ce une fois pour toutes.
    Si caque bout de ton programme qui a besoin d'envoyer/recevoir des données les encode/décode à la main, non seulement tu vas réécrire des centaines de fois les même bouts de code, mais surtout la moindre erreur et c'est l'ensemble de ton flux de données (donc la connexion) qui est corrompu (et trèèès difficile à débugguer car le plantage peut survenir plus ou moins longtemps après).

    En ce qui concerne l'architecture globale, je te propose:

    Des classes communes au client et au serveur :

    - Une classe TcpFrame te permettant de stocker et de sérialiser / désérialiser des groupes de données de base (int, float, bool, String, ...). En transformant ces données en suite d'octets, elle te permettra de facilement [encoder+envoyer] puis [recevoir+décoder] des paquets de données à travers tes sockets. Elle maintient en interne un buffer d'octets dans lequel les données sont stockées.

    Les communications réseaux utiliseront systématiquement ce mécanisme pour échanger des données. Cela te permettra d'avoir un protocole définit une fois pour toutes pour l'encodage/décodage des données. Ton code 'applicatif' ne pourra alors plus 'taper' directement (écrire, lire) dans la socket en risquant de corrompre le flux de données.

    exemple: EDIT: cf. message ci-dessous pour l'exemple de prototype.

    Ainsi, lorsque tu veux envoyer des données:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    TcpFrame f = new TcpFrame();
    f.add(monInt);
    f.add("mon string");
    maSocket.sendFrame(f);
    On la récupère côté récepteur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public void onDataReceived(byte[] data) {
        TcpFrame f = new TcpFRame(data);
        // l'ordre est important: il doit être le même que lors de la sérialisation.
        int monIntRecu = f.getInt();
        String strRecu = f.getString();
    }
    De plus, cela va permettre de distinguer chaque 'groupe' d'octets correspondant à un message complet (en effet, lorsque tu envoies en une fois par exemple 100 octets de données sur une socket TCP, tu peux très bien les recevoir en deux fois de l'autre côté: 10 octets, puis 90 octets un peu plus tard).

    La structure de base d'une TcpFrame sérialisée et telle qu'envoyée sur la socket correspondra typiquement à ça:

    1/ toujours 2 octets d'en-tête représentant un 'unsigned short' qui donne la taille du paquet en octets (ie. 1 + 2 + 3 + ... + x ci-dessous). Cela te permet de connaître la taille totale du paquet, et donc de savoir quand tu auras reçu sa totalité (et par voie de conséquence où le paquet suivant commence)

    2/ ta première donnée (par exemple 4 octets pour l'int ci-dessus).

    3/ ta deuxième donnée (par exemple (str.length) codé sur 2 octets + str.data) pour le string ci-dessus

    ./ etc...

    x/ ta (x-1)ième donnée...


    - une classe de TcpFrameSocket, ie. une socket TCP améliorée :
    * elle encapsulera son propre thread qui attendra la réception des données.
    * ce sera un 'observeur' et émettra des évenements : onSocketConnected(), onSocketDisconnected(), onDataReceived(TcpFrame f)
    * elle permettra d'envoyer des données, mais uniquement sous forme de TcpFrame, pour abstraire la structure interne des données échangées.
    * elle s'occupera du bon découpage/aggrégation des paquets de données reçus et leur conversion en TcpFrame.

    Les classes spécifiques au côté serveur:

    - une classe ServerSocket qui aura également son propre thread et attendra les nouvelles connection sur un port. Lorsqu'un client se connecte, elle créera la socket associé à cette connexion, et émettra un événement genre 'onClientConnected(TcpFrameSocket s)

    - une classe 'ConnexionPool' qui recensera l'ensemble des clients actuellemnt connectés.
    * Elle contiendra une table associative (voir plusieurs) qui associeront pour chaque client un objet contenant les informations sur ce client (par exemple pseudo, identifiant unique, ...). Appelons ces informations 'UserDetails'
    * Elle pourra émettre des événements : onNewClientConnected(UserDetails), onClientDisconnected(UserDetails), onFrameReceived(UserDetails fromUser, TcpFrame frame)

    * On s'en doute: ConnexionPool écoutera les événements d'un ServerSocket (pour les nouvelles connexions) et de chacune des Socket client (pour les données et les déconnexions). A chaque connexion/déconnection d'un client, la table associative sera mise à jour.

    A partir de là, la partie 'application' de ton serveur à proprement parler n'aura plus qu'à s'interfacer avec ConnexionPool. Elle ne verra plus les Sockets directement, mais des instances de UserDetails. Elle aura juste à sa disposition :
    * les événements pour être prévenue lorsque des données / connexion / déconnexions arrivent.
    * une méthode pour envoyer une TcpFrame à un, plusieurs, voir à tous les clients connectés (en itérant sur la table associative de ConnexionPool). Exemple: sendMessageToUser(UserDetails user, TcpFrame f)

    Le côté client:

    Rien de bien spéficique à ce stade : étant donné qu'on n'a qu'une seule connexion à gérer, la partie 'applicative' de ton client pourra directement s'interfacer avec la TcpFrameSocket (via les événements, bien entendu + une fonction TcpFrameSocket::sendFrame(TcpFrame f))

    thread et synchronisation:
    - pour être certain de ne pas rencontrer de soucis de concurrence côté serveur, tu peux implémenter un seul mutex dans ConnexionPool : lorsque les sockets émettront un événement (auquel ConnexionPool est relié), le mutex s'assurera qu'on ne traite pas un autre événement en même temps.

    Ainsi, quoi qu'il se passe, les événements (connexion/déconnexion/données réçues et envoyées) seront toujours exécutés les uns à la suite des autres.

    - côté client, il faudra juste prévoir le même genre de principe entre ta socket qui a son thread et le reste de l'appli.



    Voilà dans les grandes lignes.

    C'est le premier niveau. Il y a encore beaucoup, beaucoup de place pour améliorer cette base, notamment:

    - côté serveur, au lieu d'avoir un thread par socket, faire un thread qui écoute toutes les sockets en même temps (cf. Java NIO, l'équivalent existe en C/C++). C'est plus efficce pour gérer un grand nombre de connexions, et ça élimine de fait tout risque de concurrence (un seul thread), donc le besoin du Mutex dans ConnexionPool.

    - faire un niveau d'abstraction au dessus de ConnexionPool pour pouvoir séparer les communications en 'canaux' logiques. Par exemple un canal pour gérer les déplacements des persos, un autre pour le chat, ...
    Chaque TcpFrame échangée sur le réseau sera destinée à un canal unique. Chaque partie applicative qui voudra envoyer/recevoir des données le fera via un unique canal. Cela permet de faire autant de paires de classes différentes (une pour le client + une pour le serveur), donc de séparer ton code de façon logique (et donc d'augmenter la réutilisabilité et minimser les bugs). Par exemple:
    * ClientChatManager & ServerChatManager qui s'occuperont uniquement de l'émission / réception des messages du chat. Il utiliseront leur canal 'x' pour communiquer.
    * ClientUserMoveManager & ServerUserMoveManager qui s'occuperont uniquement de la mise à jour de la position des personnages. Ils utiliseront leur cana 'y' pour communiquer
    Ainsi, les objets enregistrés pour le canal 'x' (le chat) ne recevront des événements que lorsqu'un paquet étiqueté 'x' sera reçu ; de plus, cela garantit que personne d'autre qu'eux ne pourra voir les paquets étiquetés 'x'.


    Il y a plein d'autres choses à améliorer encore, mais tu as déjà une bonne base capable de gérer quelques milliers de clients, et réutilisable dans la majorité des applications réseaux que tu voudras développer par la suite

  3. #3
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Pour le plaisir: un prototype des classes mentionnées ci-dessus. Syntaxe style Java (car plus parlante pour la notion de classe concrète vs. interface) et syntaxe probablement incorrecte car j'ai écris ça à main levée.

    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
    public class TcpFrame {
        // construire une TcpFrame qu'on va remplir pour l'envoyer
        public TcpFrame(); 
    
        // construire une TcpFrame à partir de données reçues d'une socket
        // en vue de décoder et lire les données.
        public TcpFrame (byte[] data);
    
        public int size();
        public boolean isEndOfFrame();
    
        // utile pour TcpFrameSocket.sendTcpFrame()
        public byte[] getInternalData(); 
    
        public void addInt(int i);
        public int getInt();
        public void addShort(short i);
        public short getShort();
        public void addUnsignedShort(int i);
        public int getUnsignedShort();
        public void addByte(int i);
        public int getByte();
        public void addBoolean(boolean b);
        public boolean getBoolean();
        public void addString(String str);
        public String getString();
        public void addFloat(float f);
        public float getFloat();
    }
    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
    public class TcpFrameSocket implements Runnable {
        public static interface Listener {
            abstract public void onSocketConnected(FrameSocket socket);
            abstract public void onSocketConnectionError(FrameSocket socket, ConnexionErrorReason reason);
            abstract public void onFrameReceived(FrameSocket socket, TcpFrame tcpFrame);
            abstract public void onFrameSent(FrameSocket socket, TcpFrame tcpFrame);
            abstract public void onSocketDisconnected(FrameSocket socket, boolean disconnectedByPeer);
        }
    
        public enum ConnexionErrorReason {
            HOST_NOT_FOUND,
            HOST_CONNEXION_REFUSED,
            CONNEXION_TIMED_OUT
        }
    
        protected Socket m_socket;
        protected Thread m_thread;
    
        public TcpFrameSocket();
    
        public void addListener(Listener l);
        public removeListener(Listener l);
    
        public boolean isConnected();
        public boolean connectToHost(String theHost, int thePort);
        public void disconnect();
    
        // l'unique façon 'publique' d'envoyer des données à un client.
        public void sendTcpFrame(TcpFrame f);
    
        public static TcpFrameSocket createSocketFromServerSocket(Socket clientSocket);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class ServerSocket implements Runnable {
    
        public static interface Listener {
            public void onNewClientConnected(TcpFrameSocket socket);
        }
    
        public ServerSocket();
    
        public void addListener(Listener listener);
        public void removeListener(Listener listener);
        public boolean startListening(int port);
        public void stopListening();
    }
    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
    public class ConnexionPool implements TcpFrameSocket.Listener, ServerSocket.Listener {
        public static interface Listener {
            public void onUserConnected(UserDetails u);
            public void onUserLeftWorld(UserDetails u);
        }
    
        // les 2 tables associatives, qui contiennent toutes les 2 les mêmes entrées
        // mais organisées de façon différente pour récupérer rapidement l'info dont on a besoin:
    
        // pour récupérer les UserDetails d'un client lorsqu'on connait sa socket (utile pour 'onFrameReceived(UserDtails, TcpFrameSocket)').
        private Map<FrameSocket,UserDetails> m_userBySocket = new Hashtable<FrameSocket,UserDetails>();
        // pour récupérer la socket d'un client quand on connait son UserDetails (utile pour 'sendMessageToClient(UserDetails u, TcpFrame f)')
        private Map<UserDetails, FrameSocket> m_socketByUser = new Hashtable<Integer, FrameSocket>();
    
        public ConnexionPool(ServerSocket s) {
            s.addListener(this);
        }
        
        public final void addListener(Listener listener);
        public final void removeListener(Listener listener);
        public UserDetails getUser(int id);
        public List<UserDetails> getAllUsers();
    
        // la seule façon 'publique' d'envoyer des données.
        public void sendMessageToOneClient(UserDetails user, TcpFrame message);
        public void sendMessageToAll(TcpFrame message);
    
        // implémentation de l'interface FrameTcpSocket.Listener
        public void onFrameReceived(FrameSocket sock, TcpFrame frame);
        public void onSocketDisconnected(FrameSocket socket, boolean disconnectedByPeer);
    
        // implémentation de l'interface ServerSocket.Listener
        public void onNewClientConnected(TcpFrameSocket socket);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public class UserDetails {
        int id;
        String pseudo;
    
        public UserDetails(int id, String pseudo);
        public int getId();
        public String getPseudo();
    }
    Tu y trouveras notamment la notion d'observeur (les interfaces 'Listener') et tu remarqueras que ConnexionPool:
    - il peut-être 'observé' (il émet des événements lorsque qu'un client se connecte/déconnecte/a envoyé des données.
    - observe lui même une instance de ServerSocket et l'ensemble des Socket des clients connectés.

    La partie applicative du serveur est alors bien abstraite de toute l'implémentation 'bas niveau' pour l'échange des données. En effet, elle ne touchera plus qu'à:
    - ConnexionPool.sendMessageToUser() pour envoyer un message
    - ConnexionPool.Listener pour être prévenu des connexions/déconnexions et de l'arrivée des messages.
    - Une classe UserDetails qui abstrait la notion de client et facilement dérivable pour ajouter ses propres données spécifiques à l'application, plutôt qu'une Socket dans laquelle on taperait directement.

    Côté client, on a à peu près la même chose: on s'abstrait de l'encodage/décodage bas niveau + groupement/séparation des blocs de données en travaillant exclusivement avec des TcpFrame. De même, la gestion des connexions/déconnexions est plus simple puisque les classes qui ont besoin d'être prévenue n'ont qu'à implémenter le bon Listener.

  4. #4
    Membre éclairé
    Avatar de ZouBi
    Inscrit en
    Octobre 2007
    Messages
    508
    Détails du profil
    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 508
    Points : 812
    Points
    812
    Par défaut
    ok, merci.
    Va falloir que je relis plusieurs fois pour bien comprendre.

    Mais je pense que la librairie que j'utilise, SDL_Net n'est pas vraiment le mieux.
    En aurais tu un bien à m'conseiller?

  5. #5
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Citation Envoyé par ZouBi Voir le message
    Mais je pense que la librairie que j'utilise, SDL_Net n'est pas vraiment le mieux.
    En aurais tu un bien à m'conseiller?
    A vrai dire, la solution que je te propose au dessus est plutôt 'générique' et peut par exemple s'appliquer à une implémentation en java, comme une implémentation en C++.

    Je ne connais pas personnellement SDL_Net. J'ai jeté un oeil, et il semble que c'est une surcouche aux librairies de sockets 'classiques' (windows, unix) et qui apporte uniquement la portabilité (corrigez-moi si je me trompe).

    L'avantage, c'est que ça ressemble fortement aux librairies de socket 'classiques'. En un sens c'est un bien, car ça peut te permettre de te familiariser avec des choses proches des standards. D'un autre côté, c'est relativement bas-niveau, donc il y aura peut-être un peu plus de boulot.

    Si tu recherches quelque chose de plus évolué, je ne saurais trop quoi te conseiller.

    Je connais bien le framework Qt qui a une partie dédiée aux sockets et également un mécanisme de signaux / slots déjà intégré (équivalent des observeurs/listeners évoqués ci-dessus). tu as donc déjà des QTcpSocket avec des signaux, genre 'connected', 'disconnected', 'dataReadyRead', 'onNewClient' pour le QTcpServer, etc...
    Leur implémentation est assez haut-niveau et permet de ne même plus avoir à se soucier du multi-threading.
    De plus, il propose plein d'autres parties (widgets/IHM, dessin 2D, openGl, son, vidéo, ...) et est multi-plateforme (windows, linux, MacOs).
    Mais Qt est plutôt 'un tout', donc c'est plutôt le genre de framework qu'on utilise dès le début du projet, car l'intégrer en cours de route me semble moins immédiat. Après, tout dépend de la quantité de code déjà écrit, des dépendances vers les autres librairies, etc...

    My 2 cents.

  6. #6
    Membre éclairé
    Avatar de ZouBi
    Inscrit en
    Octobre 2007
    Messages
    508
    Détails du profil
    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 508
    Points : 812
    Points
    812
    Par défaut
    oui, je connais très bien celui de Qt.
    Le soucis, c'est que mon "moteur" de jeu est en SDL.
    Bon, je pourrai le refaire sous Qt, mais Qt pour le 2D, et jeux, c'pas terrible je trouve.
    Sinon, c'est vrai que Qt pour les sockets, c'est les mains dans les poches...
    Je vais y reflechir, merci beaucoup !

  7. #7
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    [un peu hors sujet, mais bon]

    Citation Envoyé par ZouBi Voir le message
    Bon, je pourrai le refaire sous Qt, mais Qt pour le 2D, et jeux, c'pas terrible je trouve.
    Je ne sais pas à quelle version de Qt tu fais référence, mais depuis Qt 4.0 et jusqu'à maintenant (Qt 4.4), la partie 2D a été grandement améliorée.

    Je ne sais pas en quoi consiste ton jeu exactement, mais si par "2d", tu entends 'afficher des sprites', les déplacer, faire des zooms, rotations, etc... je te conseille vivement de te pencher sur la partie Graphics view qui implémente déjà tout ça:
    En gros, une instance de QGraphicsItem est un élément 2D affichable dans une vue (genre un 'sprite' en bitmap, en SVG, un texte, ...). Et à partir de la tu peux les déplacer, effectuer des rotations, les animer, gérer les clics dessus, ...

    [/hors sujet]

  8. #8
    Membre expérimenté
    Avatar de granquet
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2005
    Messages
    1 201
    Détails du profil
    Informations personnelles :
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2005
    Messages : 1 201
    Points : 1 421
    Points
    1 421
    Par défaut
    Citation Envoyé par nouknouk Voir le message
    /!\ pavé following !
    pas grand chose d'important à redire (même si j'ai pas exactement la même vision de la "chose")
    sauf que je ne conseille pas trop les archi server multithreadé qui semblent plus simple à developper au départ, mais qui posent finalement plus de soucis (et surtout des prises de tête pour le debug) qu'une conception bien pensé avec un select/poll.

    en tout cas chapeau, c'est plutôt bien écrit et digeste

  9. #9
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Salut
    Citation Envoyé par Dark_Ebola Voir le message
    sauf que je ne conseille pas trop les archi server multithreadé qui semblent plus simple à developper au départ, mais qui posent finalement plus de soucis (et surtout des prises de tête pour le debug) qu'une conception bien pensé avec un select/poll.
    Qt doit avoir a peu prés toute les base pour faire sans trop de problème ce serveur multithreadé (event loop, thread, reseaux, signal/slot ...)

    Boost à surement une bonne partie aussi.

  10. #10
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Citation Envoyé par Dark_Ebola Voir le message
    pas grand chose d'important à redire (même si j'ai pas exactement la même vision de la "chose")
    Ben moi ça m'intéresse à bloc d'en discuter, la confrontation des différents 'angles de vue' est souvent la meilleure façon d'arriver à une architecture sympa.
    Donc n'hésite pas (et ... pas besoin de pavé )

    je ne conseille pas trop les archi server multithreadé qui semblent plus simple à developper au départ, mais qui posent finalement plus de soucis (et surtout des prises de tête pour le debug) qu'une conception bien pensé avec un select/poll.
    - Effectivement, il y a relativement peu d'intérêt hormis un : cela me semble plus simple de travailler avec un thread par socket au début, quand on n'est pas familiarisé avec le dév. réseau. En effet, autant apprendre une chose à la fois et commencer par les sockets+threads 'classiques' où on trouve des centaines de tutos sur le net. Une fois ces concepts bien maitrisés, il sera toujours temps de découvrir et apprendre un concept supplémentaire (notre fameux select/poll).

    Au passage, il me semble bon de relativiser l'avantage du select/poll: effectivement je ne vois à priori aucun désavantage à l'utiliser. Mais ce n'est pas pour autant qu'il faut croire que la solution "1 thread par client" est si limitée que ça: j'ai déjà fais des tests de montée en charge de serveur programmé en java et gérant 5000 connexions simultanées avec cette méthode (tout en restant 'réactif'), donc ça laisse un minimum de place pour voir venir.

    - Le problème de multi-threading n'en est pas un si tu as un endroit (ici ConnexionPool) qui centralise toutes les arrivées/émissions de message, car à cet endroit tu peux très simplement mettre un mutex et toute la partie applicative ne travaille alors plus que de façon séquentielle.

    - on est tous les deux d'accord sur la finalité. C'est d'ailleurs pour ça que j'ai parlé du select/poll à la fin de mon premier post dans le paragraphe 'améliorations'. Bon, j'ai appelé ça Java/NIO parce que j'avais plus d'autre nom en tête, mais on parle exactement de la même chose.

    PS: et meeeer**. J'ai encore pondu un pavé. Désolé .

  11. #11
    Membre expérimenté
    Avatar de granquet
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2005
    Messages
    1 201
    Détails du profil
    Informations personnelles :
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2005
    Messages : 1 201
    Points : 1 421
    Points
    1 421
    Par défaut
    Citation Envoyé par nouknouk Voir le message

    - Une classe TcpFrame te permettant de stocker et de sérialiser / désérialiser des groupes de données de base (int, float, bool, String, ...). En transformant ces données en suite d'octets, elle te permettra de facilement [encoder+envoyer] puis [recevoir+décoder] des paquets de données à travers tes sockets. Elle maintient en interne un buffer d'octets dans lequel les données sont stockées.
    presque d'accord sur tout, sauf leur donner la responsabilitée de maintenir le buffer, pour l'envoi je prefere me passer de buffer et send les données au fur et à mesure de leur encodage (en passant en TCP_CORK et en desactivant NAGGLE, je sais que c'est possible sur toutes les plateformes, après dans les langages interprétés comme java, je ne sais pas trop)
    ça permet d'eviter d'avoir un buffer d'envoi en userspace, qui de toute façon vas etre recopié en intégralité pour etre envoyé.
    pour la reception, comme en principe je fait du Tag-Length-Value, l'event indique à l'observer le Tag et la Length, à l'observer de passer le buffer à remplir.

    Les communications réseaux utiliseront systématiquement ce mécanisme pour échanger des données.
    je conseillerais meme de passer la majeure partie de la conception à definir un protocole et des regles d'encodage.
    un protocole adapté c'est 90% d'ennuis en moins (oui bon j'exagere surement)

    il y'as beaucoup à dire sur les protocoles
    formats texte (http par ex): simple à parser et à encoder, mais un rapport données envoyées / données utiles assez mauvais, souvent lent à parser
    formats binaires: moins facile à encoder, generalement plus "efficace"

    ensuite il faut définir les contraintes sur la connexion, si un mode connecté est un avantage ou s'il vaut mieux passer en mode dgram.

    c'est un sujet assez vaste, il y'as beaucoup à dire, et je crois qu'il est difficile de ne rien oublier

  12. #12
    Membre habitué
    Inscrit en
    Mars 2007
    Messages
    135
    Détails du profil
    Informations forums :
    Inscription : Mars 2007
    Messages : 135
    Points : 146
    Points
    146
    Par défaut Exemple utilisation select poll avec nio
    Je rebondi sur ce poste.

    Pour améliorer les principes détaillés dans vos poste y-a-t-il qque part une doc montrant comme effectuer du select/poll sur un groupe de socket java.


    Je suis assez d'accord sur un mix thread/polling. Un thread gérant plusieurs clients.

    merci

  13. #13
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Points : 2 161
    Points
    2 161
    Par défaut
    Citation Envoyé par inconnu652000 Voir le message
    y-a-t-il qque part une doc montrant comme effectuer du select/poll sur un groupe de socket java.
    Google est ton ami avec les bons mot clefs: "java nio socket client server"

    On trouve des exemples de code complet client & serveur, notamment celui-là

  14. #14
    Membre à l'essai
    Inscrit en
    Mai 2006
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 15
    Points : 16
    Points
    16
    Par défaut
    une question svp

    Comment géré les clients du coté serveur, ou les stocker et comment envoyer une command à un client X et recevoir la réponse.


    Merci

Discussions similaires

  1. [WD19] Déploiement d'une application avec HFSQL client/serveur
    Par jjacques68 dans le forum WinDev
    Réponses: 1
    Dernier message: 03/11/2014, 08h39
  2. Réponses: 0
    Dernier message: 17/03/2008, 12h29
  3. [wd9] rendre une application monoposte en client/serveur
    Par hamdi amine dans le forum WinDev
    Réponses: 2
    Dernier message: 11/02/2008, 12h53
  4. developement d'une application d'authentification client/serveur
    Par anisj1m dans le forum Général Java
    Réponses: 2
    Dernier message: 22/02/2007, 12h02
  5. Réponses: 5
    Dernier message: 08/01/2004, 16h48

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