Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 7 sur 7
  1. #1
    Membre expérimenté

    Homme Profil pro
    Doctorant en astrophysique
    Inscrit en
    juin 2007
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en astrophysique

    Informations forums :
    Inscription : juin 2007
    Messages : 365
    Points : 531
    Points
    531

    Par défaut Première expérience en réseau : modèle pour client/serveur (tour par tour)

    Bonjour,

    Pour la toute première fois, je m'initie aux joies de la programmation réseau sur un petit projet personnel : une esquisse de jeu de stratégie/exploration au tour par tour. Pas de grosses contraintes sur les performances réseau donc, mais j'aimerais quand même faire les choses bien.

    Voici le modèle que j'ai en tête pour le moment :

    Légende : Les boites claires en petit pointillés représentent les différents processus qui sont mis en réseau (serveur, client, etc.), qui peuvent être sur une même machine ou non (par exemple un joueur peut lancer le serveur sur sa machine et jouer en même temps).
    Les boites situées à l'intérieur distinguent les différents threads au sein d'un même processus.
    Les flèches en trait plein montrent les interactions réseau (TCP?), celles en pointillés les interactions entre threads (je pense à une communication basée sur une queue d'événements/messages).

    Côté serveur :
    • Worker est le thread qui va faire le gros du travail, i.e. toute la gestion du jeu (IA, pathfinding, etc.). C'est lui qui est responsable de l'état du serveur à tout instant. Il est possible que je décide par la suite de créer des sous-threads qui vont s'occuper d'une tâche précise et lourde (sauvegarde automatique, calcul du tour d'une IA) pour laisser le champ libre à d'autres tâches plus simples que peuvent demander d'autres clients au même moment.
    • Dispatcher écoute les actions des clients (déplacement d'une unité, demande de fin de tour ...) et les transmet à Worker qui va les répercuter sur l'état du serveur. C'est lui qui se charge aussi d'envoyer des messages aux différents clients (annonçant par exemple le début de son tour à un client).


    Côté client (client simple) :
    • GUI (game user interface) se charge d'afficher joliment l'état du jeu au client, et lui permet d'agir sur le jeu (donner des ordres, etc.). Maintient une copie partielle de l'état du jeu (seulement les unités qui sont visibles par ce joueur) construite à partir des instructions du serveur.
    • Net. Com. (network communication) se charge d'écouter et transmettre à GUI tout message provenant du serveur (confirmation d'action du client, actions visibles des autres clients) et d'envoyer des messages au serveur (demande d'action, etc.).


    Côté admin (client admin) :
    • CLI (command line interface) est un thread de gestion en ligne de commande du serveur. C'est par cette interface sobre qu'un admin peut envoyer des commandes au serveur, demander au serveur des informations sur sa configuration, sur la partie en cours, mais aussi sur les différents clients (adresses IP en cas de bannissement, etc), bref tout un lot de requête qui seront a priori refusées aux clients normaux (via un simple système de mot de passe ?). Il est possible que cet élément disparaisse par la suite, mais je pense que ça me sera tout de même bien utile pour démarrer.
    • Net. Com., comme pour un client usuel.


    Est-ce que vous voyez un gros soucis dans cette approche, ou une chose qui m'aurait échappée ? La seule expérience que j'ai en réseau est ce que j'ai pu apprendre en jouant : il y a donc sans doute aussi des points qui sont un peu naïfs...

    Merci d'avance pour votre avis en tout cas !

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

    Informations forums :
    Inscription : décembre 2006
    Messages : 1 637
    Points : 2 010
    Points
    2 010

    Par défaut

    Salut,

    Tu as plusieurs discussions qui traitent du même sujet où d'un sujet très proche, et qui sont déjà dispo sur le forum. Une petite recherche devrait te donner quelques résultats intéressants (genre celui-ci, et ceux qui y sont référencés).

    Je te conseille de faire une petite 'balade' dans le forum pour y trouver sans doute quelques réponses, quitte à revenir ici ensuite si certaines questions restent en suspens.
    Mon projet du moment: BounceBox, un jeu multijoueurs sur Freebox, sur PC et depuis peu sur smartphone/tablette Android.

  3. #3
    Membre régulier
    Homme Profil pro
    Architecte serveur
    Inscrit en
    septembre 2011
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte serveur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2011
    Messages : 64
    Points : 94
    Points
    94

    Par défaut

    Bonjour.

    Pour moi, tu te diriges vers une architecture classique (mais pas simple) : l'architecture standard d'un serveur de MMO.
    Les principaux soucis que tu vas rencontrer ne sont pas au niveau des grosses flèches noires (surtout si tu te bases sur une bibliothèque réseau stable), ni des différents clients (d'ailleurs, en général, on en fait qu'un, tu verras que ton CLI s'intègre parfaitement à l'intérieur de ton GUI), mais de la petite flèche pointillée qui se trouve entre ton worker et ton dispatcher.
    En effet, c'est là que tu vas rencontrer le plus gros stress (enfin, sauf à ce que ton client soit codé en 3D top moumoute, mais ça n'a pas l'air d'être le cas).
    Enfin, tout comme mon prédécesseur, je te redirige vers une discussion qui a déjà eu lieu sur les architectures de MMO : http://www.developpez.net/forums/d10...deamon-mmorpg/
    Je pense que tu y trouveras au fil des échanges pas mal d'informations utiles.

    Sinon, si ton objectif est d'appréhender pleinement le développement d'un serveur, je t'encourage à exporter tes IAs sur des clients virtuels. En gros, plutôt que d'avoir des IAs différentes des joueurs, créer des IAs qui se connectent et jouent comme un vrai joueur. Ca te permettra 2 choses :
    - Stress tester ton serveur (et donc le faire bugger à foison), ce qui te fera découvrir ce qui est pour moi la partie la plus importante du développement serveur : Le débugging.
    - Découvrir une bonne pratique dans le développement d'un serveur. Tester le client virtuel, c'est l'adopter. Parce que les tests unitaires ne sont pas suffisants pour valider un serveur, vu qu'ils sont unitaires, et que les principaux bugs sur un serveur sont dus justement à des enchaînements et superpositions d'actions similaires.

  4. #4
    Membre expérimenté

    Homme Profil pro
    Doctorant en astrophysique
    Inscrit en
    juin 2007
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en astrophysique

    Informations forums :
    Inscription : juin 2007
    Messages : 365
    Points : 531
    Points
    531

    Par défaut

    Citation Envoyé par SuperBidi Voir le message
    Pour moi, tu te diriges vers une architecture classique (mais pas simple) : l'architecture standard d'un serveur de MMO.
    Arg... je ne pensais pas avoir des prétentions si élevées J'ai commencé en suivant cette voie là (à défaut de mieux), et je n'ai pour l'instant eu le temps de travailler sur le client admin (connexion, authentification par mot de passe, et commande pour demander au serveur de s'éteindre), mais pour l'instant ça n'a pas l'air trop dur.

    Citation Envoyé par SuperBidi Voir le message
    Les principaux soucis que tu vas rencontrer ne sont pas au niveau des grosses flèches noires (surtout si tu te bases sur une bibliothèque réseau stable), ni des différents clients (d'ailleurs, en général, on en fait qu'un, tu verras que ton CLI s'intègre parfaitement à l'intérieur de ton GUI), mais de la petite flèche pointillée qui se trouve entre ton worker et ton dispatcher.
    En effet, c'est là que tu vas rencontrer le plus gros stress (enfin, sauf à ce que ton client soit codé en 3D top moumoute, mais ça n'a pas l'air d'être le cas).
    Pour l'interaction entre les threads, j'utilise une queue non bloquante (cf. cet article de Herb Sutter) dans chaque thread, couplée au système de paquet de la SFML que je trouve très élégant (je l'utilise aussi pour le réseau). De fait, les threads communiquent entre eux seulement par message (je commence chaque paquet par un entier qui donne la nature du message, suivi de données supplémentaires si nécessaire).

    Par exemple, le thread de communication réseau (C) possède deux queues, une "in" et une "out". Il remplit la queue "in" avec les paquets réseau reçus, qui sont alors consommée par le thread de travail (W), et consomme la queue "out", qui contient les résultats émis par (W). Avec seulement un thread de travail c'est assez simple, mais je pourrais plus tard en rajouter d'autres et donner la responsabilité à (C) de choisir à quel thread envoyer le boulot.

    (Pour le CLI, oui je vais probablement intégrer ses fonctionnalités au client GUI, mais j'aime avoir la possibilité de contrôler le serveur sans avoir à lancer une appli GUI a priori gourmande juste pour lancer deux ou trois commandes.)

    Citation Envoyé par SuperBidi Voir le message
    Sinon, si ton objectif est d'appréhender pleinement le développement d'un serveur, je t'encourage à exporter tes IAs sur des clients virtuels.
    C'est une très bonne idée, je n'y avais pas pensé !

    Merci pour ces remarques et le lien.

  5. #5
    Membre régulier
    Homme Profil pro
    Architecte serveur
    Inscrit en
    septembre 2011
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte serveur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2011
    Messages : 64
    Points : 94
    Points
    94

    Par défaut

    Citation Envoyé par Kalith Voir le message
    Arg... je ne pensais pas avoir des prétentions si élevées J'ai commencé en suivant cette voie là (à défaut de mieux), et je n'ai pour l'instant eu le temps de travailler sur le client admin (connexion, authentification par mot de passe, et commande pour demander au serveur de s'éteindre), mais pour l'instant ça n'a pas l'air trop dur.
    Ce qui distingue le serveur de MMO de la majorité des autres technos serveur, c'est le multi-thread. Je parle bien sûr d'un vrai multi-thread, et non pas d'une multiplicité de mono threads (comme peut l'être un serveur PHP).

    Citation Envoyé par Kalith Voir le message
    Pour l'interaction entre les threads, j'utilise une queue non bloquante (cf. cet article de Herb Sutter) dans chaque thread, couplée au système de paquet de la SFML que je trouve très élégant (je l'utilise aussi pour le réseau). De fait, les threads communiquent entre eux seulement par message (je commence chaque paquet par un entier qui donne la nature du message, suivi de données supplémentaires si nécessaire).

    Par exemple, le thread de communication réseau (C) possède deux queues, une "in" et une "out". Il remplit la queue "in" avec les paquets réseau reçus, qui sont alors consommée par le thread de travail (W), et consomme la queue "out", qui contient les résultats émis par (W). Avec seulement un thread de travail c'est assez simple, mais je pourrais plus tard en rajouter d'autres et donner la responsabilité à (C) de choisir à quel thread envoyer le boulot.
    Structure sur laquelle j'ai bossé à la fois en tant que dév serveur, puis que j'ai reproduit en tant qu'architecte. Ca marche très bien. Et c'est très efficace (et pas dur à utiliser). Juste, tu n'as besoin que d'une queue in par thread, pas besoin de out (vu que l'out, c'est l'in d'un autre thread). De même, tu verras qu'en règle général, tous les messages d'un type vont vers un thread donné. Donc tu peux, en plus de l'id de message, avoir une id du thread de réception directement.
    De même, tu peux transmettre des messages non sérialisés entre tes threads. La sérialisation n'est nécessaire que si tu envoies au client. Comme ça, tu te fais pas suer à sérialiser les messages purement internes au serveur.
    Et pour finir, même pas besoin d'une queue non bloquante. Vu que tu fais extrêmement peu d'opérations sur ta queue (un if (!empty()) pop(); en lecture, et un push(); en écriture).

    En fait, les difficultés du serveur de MMO se trouvent sur 2 points :
    - Connexion/déconnexion. Clairement une partie sur laquelle tu peux passer 3 mois, dont la moitié en débugging. Le soucis, c'est que tu dois propager ta déconnexion sur les multiples threads en prenant en compte toutes les possibilités (client qui perd la connexion alors qu'il est en train de se connecter, client en train de faire une action bloquante avec un autre client, etc...). Et bien entendu, le plus drôle : La synchro avec la bdd. Vu que les actions faites sur la bdd mettent du temps avant de se terminer, toute déconnexion peut se faire au milieu d'un traitement bdd (qui peut être attendu pour finir un autre traitement, comme dans le cas d'un select). La gestion du contexte client est pour moi la grosse partie du serveur de MMO.
    - Le débugging. Là, ça dépend beaucoup de ton désir de rendre le serveur stable, et donc des langages impliqués. Le Java permet de cacher des erreurs, par exemple, quand le C++ va cracher à la moindre de celles ci. Maintenant, si tu veux que ton serveur soit vraiment stable et accueille un minimum de joueurs, tu vas découvrir la joie des bugs avec des occurrences hyper faible (1 sur 1000, voire 1 sur 1 million si tu veux vraiment pousser jusqu'au bout et atteindre les stabilités qu'on attend pour tenir quelques milliers de connectés).

    Donc, très clairement, pour moi, un très gros boulot. A titre d'exemple, développer le serveur de Drakerz m'a pris un peu plus d'une année à temps plein. Bon, ton serveur sera plus simple, mais compte bien 6 mois si tu veux faire un truc carré et sérieux (et stable).
    Par contre, c'est hyper intéressant. Outre le fait que l'intérieur du serveur est composé de tas de magnifiques algorithmes, le moindre bug que tu vas rencontrer va te demander des trésors d'ingéniosité pour être éliminé (ho, la belle leak). Faut être un poil hardcore pour aimer ça

  6. #6
    Membre expérimenté

    Homme Profil pro
    Doctorant en astrophysique
    Inscrit en
    juin 2007
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en astrophysique

    Informations forums :
    Inscription : juin 2007
    Messages : 365
    Points : 531
    Points
    531

    Par défaut

    Citation Envoyé par SuperBidi Voir le message
    Juste, tu n'as besoin que d'une queue in par thread, pas besoin de out (vu que l'out, c'est l'in d'un autre thread). De même, tu verras qu'en règle général, tous les messages d'un type vont vers un thread donné. Donc tu peux, en plus de l'id de message, avoir une id du thread de réception directement.
    En fait quand je parlais de threads, je n'incluais pas les threads principaux (i.e. Worker et GUI dans mon graphe). En fin de compte, les queues ne se trouvent quand dans les threads de communication réseau (mais il y en a bien deux dans chaque, une "in" et une "out"). Comme que j'ai que deux threads par processus, ça revient au même

    Citation Envoyé par SuperBidi Voir le message
    De même, tu peux transmettre des messages non sérialisés entre tes threads. La sérialisation n'est nécessaire que si tu envoies au client. Comme ça, tu te fais pas suer à sérialiser les messages purement internes au serveur.
    Que veux-tu dire exactement par sérialiser ? Le système de paquet de la SFML permet de stoker un nombre arbitraire de valeurs de manière contigüe en binaire. Je ne pense pas qu'il y ait une grande différence de perf par rapport au fait de passer une structure camouflée dans un gros void* (ou avec du type erasure si on veut être plus propre), et l'avantage principal du sf::Packet est que je n'ai pas à créer une structure pour chaque type de message, vu qu'on le lit et remplit via :
    Code c++ :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    sf::Packet p;
     
    // Émission
    int i = 5;
    float f = 12;
    std::string s = "coucou"
    p << i << f << s;
     
    // Réception
    int i;
    float f;
    std::string s;
    p >> i >> f >> s;

    Citation Envoyé par SuperBidi Voir le message
    Et pour finir, même pas besoin d'une queue non bloquante. Vu que tu fais extrêmement peu d'opérations sur ta queue (un if (!empty()) pop(); en lecture, et un push(); en écriture).
    Tu suggère de l'utiliser "nue", ou avec des mutex plutôt ? L'implémentation de cette queue non bloquante par Sutter est quand même ridiculement simple, et avec une perte pratiquement nulle (obligé d'avoir toujours un élément vide et inutilisable dans la queue). Ça serait dommage de s'en priver je trouve, vu la garantie de thread safety qu'elle propose.

    Citation Envoyé par SuperBidi Voir le message
    - Connexion/déconnexion.
    Ça devrait être gérable sur le genre de projet auquel je m'attelle, vu qu'il n'y aura jamais beaucoup de clients connectés sur une partie (10 au max par exemple, en comptant les IAs), et qu'il n'y a pas de contrainte de temps réel trop forte. Maintenant, comme je n'ai encore jamais fait ça, il a sans doute plein de problèmes auxquels je ne pense pas et qui vont me tomber dessus par la suite

    J'ai vu que tu parlais de bdd aussi sur l'autre sujet, mais je ne pense pas en utiliser ici. Le volume de donnée sera a priori suffisamment faible pour tout garder en RAM dans un processus (en tout cas je ferais en sorte que ce soit le cas).

    Citation Envoyé par SuperBidi Voir le message
    - Le débugging.
    Ca sera du C++, mais je pense coder assez proprement pour que ça ne crash pas toutes les 5 minutes. L'avenir en jugera

    Citation Envoyé par SuperBidi Voir le message
    Bon, ton serveur sera plus simple, mais compte bien 6 mois si tu veux faire un truc carré et sérieux (et stable).
    J'espère que tu ne parles pas de temps plein ici aussi... Sinon je crains de ne pas pouvoir y arriver avant ma mort, étant donné le peu de temps que j'ai à consacrer à ça

    Merci de partager ton expérience en tout cas, c'est très intéressant.

  7. #7
    Membre régulier
    Homme Profil pro
    Architecte serveur
    Inscrit en
    septembre 2011
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte serveur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2011
    Messages : 64
    Points : 94
    Points
    94

    Par défaut

    Citation Envoyé par Kalith Voir le message
    Que veux-tu dire exactement par sérialiser ? Le système de paquet de la SFML permet de stoker un nombre arbitraire de valeurs de manière contigüe en binaire. Je ne pense pas qu'il y ait une grande différence de perf par rapport au fait de passer une structure camouflée dans un gros void* (ou avec du type erasure si on veut être plus propre), et l'avantage principal du sf::Packet est que je n'ai pas à créer une structure pour chaque type de message, vu qu'on le lit et remplit via :
    Sérialiser = transformer une structure de données en série binaire.
    Par contre, je serais toi, j'encapsulerai tes messages dans des objets. Ca te permettrait de centraliser la sérialisation/désérialisation en appelant à chaque fois une méthode de l'objet (plutôt que de la faire dans la méthode d'exécution du message, ce qui fout un poil plus le bordel, et surtout, encourage une programmation fouillis).

    Citation Envoyé par Kalith Voir le message
    J'espère que tu ne parles pas de temps plein ici aussi... Sinon je crains de ne pas pouvoir y arriver avant ma mort, étant donné le peu de temps que j'ai à consacrer à ça

    Merci de partager ton expérience en tout cas, c'est très intéressant.
    Comme je disais, ça dépend jusqu'où tu veux aller. Si tu veux tenter de rendre ton serveur stable avec un peu plus d'une dizaine de personnes (donc le valider avec un client virtuel), ça peut aisément te prendre un à 2 mois. Parce que les bugs, sauf à ce que tu ais des compétences particulières dans le domaine, tout le monde en fait ^^
    Ensuite, le fait de s'affranchir d'une bdd te fait gagner sur la connexion, mais quand même pas tant que ça. Un nouveau joueur qui se connecte devra récupérer le contexte global. Sauf à ce que tu lui crées un nouveau compte et qu'il recommence de 0 (mais là, c'est limite à se demander pourquoi tu as besoin d'un serveur si tu peux exécuter tout côté client ^^).
    Enfin, j'ai parlé de six mois pour faire un truc qui ressemble à un serveur. Le gros avantage de terminer la structure et de la débugger, c'est que c'est réutilisable sur plein d'autres projets. Et à toi la myriade de petits projets client/serveur développés rapidement

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •