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 2D, 3D et Jeux Discussion :

[conception] architecture client/serveur et données partagées


Sujet :

Développement 2D, 3D et Jeux

  1. #1
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut [conception] architecture client/serveur et données partagées
    Bonjour,

    une petite question de conception.
    Imaginons qu'on travaille sur un jeu de rôle avec une architecture client/serveur, et que les 2 parties sont deux exécutables différents. Prenons par exemple les caractéristiques du personnage (force, dextérité, ...). Disons, pour l'exemple, que ces caractéristiques sont stockées dans une structure C. Ces données sont nécessaires des deux côtés: du côté serveur pour calculer les résultats des combats, et du côté client, ne serait-ce que pour les afficher.

    J'aurais donc tendance à déclarer ma structure C dans une partie commune aux deux programmes. Cette solution a plusieurs avantages, et en particulier pour la maintenance (pas de duplication de code, etc.). D'autant plus que ces données vont transiter entre le client et le serveur (donc il y a la gestion des transferts réseaux à gérer et à maintenir).

    Mais d'un autre côté, cette solution ne me plait pas trop car je trouve que, d'un point de logique (et sécurité, bien que ça soit très secondaire pour l'instant), le client ne devrait pas connaître ces structures. Lui il se contente d'afficher, le reste il ne devrait pas s'en préoccuper.

    Alors voilà, j'hésite entre les deux approches. Si vous avez de l'expérience ou des réflexions sur le sujet, toute remarque est la bienvenue.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  2. #2
    Inactif  

    Homme Profil pro
    Développeur multimédia
    Inscrit en
    Mars 2013
    Messages
    162
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur multimédia

    Informations forums :
    Inscription : Mars 2013
    Messages : 162
    Points : 261
    Points
    261
    Par défaut
    ben il faut bien une struct commune au serveur et client pour transférer les caractéristiques du perso qui seront affichées par le client, non ?

  3. #3
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Cette solution ne me convient pas, pour plusieurs raison, que je pense pouvoir résumer en un point:
    Ce dont a besoin le client est différent que les données que possède le serveur sur le personnage.

    Par exemple, disons que le personnage a 2 caractéristiques: force et intelligence. Le client n'a donc besoin que de ces 2 valeurs. Côté serveur, on a en plus une caractéristique cachée "vitesse_deplacement", calculée en fonction des 2 autres.

    Mon problème est que si l'on envoie tout, on surchargera inutilement le réseau. Pour cet exemple ce n'est pas un problème, mais je suis dans une situation (en vrai) où c'est un problème.

    Autre solution: on peut déclarer une structure qui ne stocke que les caractéristiques communes aux deux parties. Dans notre exemple, il n'y aura que force et intelligence. Mais le problème c'est que du côté serveur, notre classe personnage sera un peu bizarre et sémantiquement difforme.

    Il doit y avoir une solution en utilisant des indirections, mais comment?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  4. #4
    Membre éprouvé

    Homme Profil pro
    Ingénieur logiciel embarqué
    Inscrit en
    Juillet 2002
    Messages
    386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur logiciel embarqué
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2002
    Messages : 386
    Points : 1 164
    Points
    1 164
    Par défaut
    Pour ma part j'ai travailler sur des partage de grosses structures (plusieurs millions d'entrées) et j'ai opté pour une solution de partage complet a l'initialisation (phase sans contraintes pour nous et le client devais afficher toutes les informations) puis un système de partage des différences au cours du développement.

    Visiblement tu ne peu pas (et tu n'a pas le besoin de) tout partager.

    Mais en lisant "caractéristique [...] calculée" dans ton 2eme post je me pose la question suivante : les valeurs calculées doivent elles avoir une existence en dehors des fonction (ou méthodes) qui les utilises?

    Car si ce n'est pas le cas, on peu se contenter de transmettre les stats du personnage pour affichage (et les mettre a jours uniquement en cas de besoin). A mois que ton personnage change de stat toutes les secondes.

  5. #5
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    En fait mon exemple était mal choisi.
    En vérité, je travaille sur un type de jeu dans lequel il y a une carte. Cette carte contient beaucoup d'informations, avec des entités qui évoluent dessus et qui sont mis a jour à chaque tick de serveur. Il serait suicidaire d'envoyer toute la map à chaque tick (j'ai choisi la méthode classique pour le serveur: un cycle = process données puis envoi mises à jour; le cycle a une fréquence donnée: 1 tick).

    Et la map en question est une agrégation de cases. Ces cases contiennent tout un tas de données dont le client n'a pas besoin (le client a juste besoin d'un ID pour savoir quel texture afficher, ainsi que 2 ou 3 paramètres concernant l'état courant de la case).

    Dans mon cas, j'ai tenté un truc mais ça ne semble pas très bien marché: chaque case a un ID. Cet ID représente le type de la case (montagne, mer, plaine...). Il sert pour l'affichage (côté client) mais aussi pour les algorithmes (côté serveur). Pour des besoins de l'architecture, ces ID sont déclarés dans un fichier de configuration (qui est envoyé au client par le serveur à l'initialisation de la partie). Le code de lecture de ce fichier et de "pré-contruction" des cases est donc commun aux deux parties. Mais je retombe sur le problème que j'expose dans mon premier message: je me retrouve, côté client, avec une classe case qui est beaucoup trop grande, et côté serveur, avec la même classe case qui demande un fichier de texture.

    Je sais pas si c'est clair :/
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  6. #6
    Membre éprouvé

    Homme Profil pro
    Ingénieur logiciel embarqué
    Inscrit en
    Juillet 2002
    Messages
    386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur logiciel embarqué
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2002
    Messages : 386
    Points : 1 164
    Points
    1 164
    Par défaut
    Je voi deux approches, on va partir du principe que tu travail avec des classes pour les présenter (mais ca se transpose bien au c):

    La première approche est classique : tu travail avec 3 classes:
    -sharedMap (qui contient les elements partagés ID et autre)
    -clientMap (qui hérite ou contient sharedMap et contient les textures)
    -serverMap (qui hérite ou contient sharedMap et contient les info serveur)

    ainsi tu a une classe partagée légère, éventuellement tu peu agréger tes info partagé dans un tableau ce qui peu simplifier les algo de partage (surtout si tu travail en C) : tu le cast en tableau de char et c'est prêt a envoyer.

    La 2eme approche que je trouve moins propre est de travailler avec une seule classe partagée qui admet des champs "null" et de traiter le problème au niveau des processus d'envoi et de réception. a l'envoi tu ne partage que les info nécessaire et a la réception tu t'assure de n'utiliser que les champs défini.

  7. #7
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Merci pour ces propositions
    La première approche me séduit. Et j'utiliserai plutôt l'agrégation que l'héritage, car je travaille en c++ et l'héritage en c++ est assez particulier et doit être utilisé avec parcimonie.
    Encore merci
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  8. #8
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Bonjour,

    Qu'entends-tu par :
    Citation Envoyé par r0d Voir le message
    l'héritage en c++ est assez particulier
    ?

    Sinon, pour ton problème, je fait généralement des vérifications côté client donc j'ai un "mini-serveur" (sorte de proxy) côté client qui met permet de vérifier que les données sont cohérentes avant d'émettre, de réduire les effets de lags et aussi de pouvoir faire des partie en LAN.

    J'ai donc dans le client un code "allégé" du serveur.
    Certaines classes de mon serveur et de mon "proxy serveur" héritent donc de classes communes.

    Et à côté, les classes du client, correspondent à la vue, elles n'ont donc rien à voir avec les classes du serveur donc pas d'héritage.

  9. #9
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par r0d Voir le message
    En fait mon exemple était mal choisi.
    J'ai été confronté à la même chose avec un jeu de carte en réseau. côté client, juste besoin de 4 infos : valeur, couleur x, y. Côté serveur, besoin de savoir dans quelle main elle est, etc...

    Tu devrais avoir deux classe pour tes cases, reprenant le principe des DTO :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class CCase
    {
       int ID;
       //etc
    };
     
    class FullCase : CCase
    {
       int monTrucSupplementaire;
       //etc
    }
    Le fichier de config c'est pareil j'aurais pas fait ça personnellement. J'aurais plutôt mis en place une base de donnée côté serveur. Les fichiers plats c'est le mal !
    Nullius in verba

  10. #10
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Neckara Voir le message
    ?
    Il y a toutes sortes de contraintes qui font que l'héritage doit être utilisé avec précaution.
    En c++ l'héritage est inclusif (la classe mère est incluse dans la classe fille) et, dû au typage fort du langage, ce sont deux types totalement distincts. Ce qui a pour conséquence, si on utilise abusivement l'héritage, de devoir faire des downcast (type mère to type fille, avec RTTI) qui sont moches, dangereux et inefficaces. Le transtipage inverse (de la classe fille à la classe mère) est gratuit et propre (puisque la mère est incluse dans la fille), mais l'inverse ne l'est pas. Un exemple concret:
    struct CCase{unsigned short unique_id};

    // case côté client, pour l'affichage graphique
    struct CaseClient : public CCase{sf::Sprite sprite;}

    // module réseau, qui reçoit l'actualisation d'une case. Le module réseau responsable de traduite les packets en cases est commun => utilisation de CCase
    class PacketParser{ vector<CCase*> & GetCasesToUpdate( const PacketList & packets; } };
    Dans cet exemple, on reçoit des CCase* qu'il va falloir transtyper en CaseClient*; c'est moche, dangereux et lent.

    La relation "est-un" est très importante dans l'héritage en c++. Il existe une phrase célèbre: "en c++, un carré n'est pas un rectangle". Car un carré se définit par un côté seulement, alors qu'un rectangle se définit par une longueur et une hauteur. Si en mathématiques, un carré est bien un type particulier en rectangle, en c++, ce n'est pas le cas.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  11. #11
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    Le fichier de config c'est pareil j'aurais pas fait ça personnellement. J'aurais plutôt mis en place une base de donnée côté serveur. Les fichiers plats c'est le mal !
    Tu mettrais un fichier de quelques Ko dans une BDD? On n'utilise pas un tracteur pour planter un clou que diantre!

    Et je ne vois pas quels sont les arguments contre l'utilisation des fichiers plat (par rapport à une BDD). C'est plus simple (sans parler du fait que pour utiliser une bdd il faut qu'elle soit installée, avec tous les problèmes de portage et de maintenance que ça implique), plus rapide (en terme d'accès aux données), plus stable, plus portable, plus modulaire...
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  12. #12
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Ah non si tu n'as que ça comme données, peut-être pas, mais je supposais qu'il y aurait dans ton jeu à la longue beaucoup plus de données brutes à stocker.
    Nullius in verba

  13. #13
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Et bien j'ai appris récemment que même sur les mastodontes tel WoW ou LoL, qui brassent des teraoctets de données par heures, ils utilisent leurs propres systèmes de gestion de données basés sur... des fichiers plats ^^
    Il n'y a en fait que les banques (et les développeurs fainéants ) qui utilisent les bdd (bon, j'exagère, parfois passer par une bdd peut être un bon choix, mais j'ai l'impression que c'est de moins en moins le cas).
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  14. #14
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    C'est pas parce que deux jeux utilisent des fichiers plats que c'est comme ça partout.
    Nullius in verba

  15. #15
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Ce qui m'étonnais beaucoup c'est que tu dises que l'héritage est "particulier" en C++, alors qu'à ma connaissances, un certains nombres de langages se comportent plus ou moins de la même manière.

    Citation Envoyé par r0d Voir le message
    Dans cet exemple, on reçoit des CCase* qu'il va falloir transtyper en CaseClient*; c'est moche, dangereux et lent.
    Avec les méthodes virtuelles, on peut déjà éviter le transtypage.
    On a aussi le double-dispatch qui permet d'éviter cela.

    Après on peut toujours utiliser un dynamic_cast<> (j'ai un doute), qui est plutôt sûr.

    Et on peut ensuite toujours définir une méthode retournant un identifiant pour un simple cast des pointeurs avec les opérateurs de convertions explicites qui sont peu sûr mais très rapide.

    Après, en C++, on use et abuse souvent des héritages, mais on peut le remplacer par un DP politique ou un DP stratégie dans certains cas.
    Mais le DP stratégie va impliquer un héritage pour définir de nouvelles stratégie.

  16. #16
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Ce qui m'étonnais beaucoup c'est que tu dises que l'héritage est "particulier" en C++, alors qu'à ma connaissances, un certains nombres de langages se comportent plus ou moins de la même manière.
    Dans certains langages (java par ex.) toutes les classes sont des pointeurs et héritent d'un super_object, ce qui change beaucoup de chose par rapport au c++. Certains langages (java par ex.) utilisent des interfaces, qui modifie considérablement l'approche objet. c++ est un des seuls langages qui permet l'héritage multiple. Dans d'autres langages (OCaml par ex.) la surcharge ne prend en compte que le nom de la fonction (c++ prend la signature). D'ailleurs, en OCaml, l'héritage n'est pas inclusif, c'est une union; dans Haskell c'est encore autre chose... Etc.

    Citation Envoyé par Neckara Voir le message
    Après on peut toujours utiliser un dynamic_cast<> (j'ai un doute), qui est plutôt sûr.
    ça c'est l'upcast (qui utilise RTTI), qui est lent et dangereux.

    Citation Envoyé par Neckara Voir le message
    Avec les méthodes virtuelles, on peut déjà éviter le transtypage.
    On a aussi le double-dispatch qui permet d'éviter cela.

    Et on peut ensuite toujours définir une méthode retournant un identifiant pour un simple cast des pointeurs avec les opérateurs de convertions explicites qui sont peu sûr mais très rapide.
    En effet, il y a plein de façons de faire. Mais c'est une question de point de vue: je préfère du code simple, quitte à ne pas respecter à la lettre quelques principes comme l'encapsulation ou cette étrange histoire de sémantique entité/valeur. D'autres idiomes me paraissent plus importants, comme Demeter ou LSP.

    Citation Envoyé par Neckara Voir le message
    Après, en C++, on use et abuse souvent des héritages, mais on peut le remplacer par un DP politique ou un DP stratégie dans certains cas.
    Mais le DP stratégie va impliquer un héritage pour définir de nouvelles stratégie.
    En fait, on se rend de plus en plus compte que l'héritage n'est finalement pas si bien que ça. Je ne le retrouve pas, mais il y un billet de blog (je croyais que c'était de E. Deloget) qui parle de ça très bien: que souvent on se jette sur l'héritage comme la faim sur le pauvre monde, mais que finalement, c'est rarement la bonne solution.
    Lorsque j'ai commencé à développer en c++ (je viens du C), j'usais et abusais de l'héritage. C'est pratique, élégant, ça semble propre. Mais petit à petit j'en reviens, car souvent ça fige trop l'architecture, et parfois ça pose de graves problèmes.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  17. #17
    Membre éprouvé

    Homme Profil pro
    Ingénieur logiciel embarqué
    Inscrit en
    Juillet 2002
    Messages
    386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur logiciel embarqué
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2002
    Messages : 386
    Points : 1 164
    Points
    1 164
    Par défaut
    Citation Envoyé par r0d Voir le message
    Lorsque j'ai commencé à développer en c++ (je viens du C), j'usais et abusais de l'héritage. C'est pratique, élégant, ça semble propre. Mais petit à petit j'en reviens, car souvent ça fige trop l'architecture, et parfois ça pose de graves problèmes.
    C'est rigolo de lire ça car a l’époque de mes études, je me battais avec mon prof a ce sujet. J'ai fini par repasser au C quant cela était possible. Puis j'ai découvert Java et j'ai été séduit par sa technique d'héritage et d'interface, bien que craintif au début.

Discussions similaires

  1. [UDP] Architecture Client/Serveur
    Par Bob.Killer dans le forum Développement
    Réponses: 1
    Dernier message: 25/04/2008, 11h09
  2. Application avec architecture client/serveur
    Par loreleï85 dans le forum Développement
    Réponses: 4
    Dernier message: 09/05/2007, 08h16
  3. MySQL en architecture client/serveur
    Par KinF dans le forum Requêtes
    Réponses: 1
    Dernier message: 07/09/2005, 22h10
  4. [Indy] Architecture Client/Serveur
    Par yongblood dans le forum Web & réseau
    Réponses: 9
    Dernier message: 22/08/2005, 01h18

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