2010-02-25 4 views
1

Je suis un peu novice en programmation C++ et je suis en train de coder un gestionnaire d'objets pour un jeu multijoueur mais j'ai des doutes sur la façon de gérer les objets client. L'objet client est composé de paramètres client connectés (comme IP, temps connecté, données reçues, etc.). Pour éviter la fragmentation de la mémoire, je prévois d'allouer un pool d'objets avec le nombre maximum de clients autorisés. Pour cela, je suis un gestionnaire de codage objet client comme ceci:Meilleures pratiques avec le gestionnaire d'objets

ClientManager.h 

#include "Client.h" 

class ClientManager { 
public: 
static void init(int max); //initialize pool (allocate memory) 
static void dispose(); //dispose pool (deallocate memory) 
static bool add(int socketFd); //add connected client by its socket file descriptor 
static bool remove(int socketFd); //remove connected client by its socket fd 
static Client& get(int socketFd); //get the client object by its socket fd 

private: 
Client* clientList; //array of allocated clients objects 
int maxClient; //max number of connected clients allowed 

Notez que cette classe sera appelée que la façon statique donc il n'y a pas de constructeurs/destructeurs. Cette classe doit être statique car il est impératif que les données client puissent être lues/modifiées entre différents types d'objets.

La mise en œuvre sera quelque chose comme:

ClientManager.cpp 

void ClientManager::init(int max) { 
maxClient = max; 
clientList = new Client[maxClient]; 
} 

void ClientManager::dispose() { 
maxClient = 0; 
delete [] clientList; 
clientList = NULL; 
} 

bool ClientManager::add(int socketFd) { 
//search pool for non-initialized object 
//if(there is a non-initializes object) { initialize it with socketFd and return true} 
//else return false; 
} 

bool ClientManager::remove(int socketFd) { 
//search pool for socketFd 
//if(socketFd found) { clear object (make it non-initialized) and return true} 
//else return false 
} 

Client& ClientManager::get(int socketFd) { 
//search for object position 
if(pos) return clientList[pos]; 
else ??????? 
} 

Maintenant, comment puis-je gérer le retour de l'objet dans la fonction get? Est-ce par référence la meilleure option? Je ne veux pas retourner un pointeur mais si c'est ma dernière option, je peux vivre avec. Je suppose que je peux m'assurer que je n'ai que l'objet enregistré (initialisé) dans le pool, si c'est le cas dans la fonction get? Je ne veux pas d'affirmation parce que je veux que le code soit robuste et ne s'arrête pas pendant l'exécution (je suis nouveau en C++ donc si je dis quelque chose de mal, corrigez-moi).

dans le programme principal, je pense à quelque chose comme:

Daemon.cpp 

#include "ClientManager.h" 

int main(...) { 
ClientManager::init(100); 

while(1) { 
//do server stuff 
//get onConnect event (new client is connecting) 
//get onDisconnect event (connected client has gone offline) 
//get onDataReceived event (connected client has sent data) 
} 
} 

void onConnect(int socketFd) { 
ClientManager::add(socketFd); 
} 

void onDisconnect(int socketFd) { 
ClientManager::remove(socketFd); 
} 

void onDataReceived(int socketFd) { 
do_something(ClientManager::get(socketFd).data); 
} 

Suis-je faire droit? Merci

Notes:
1) Ce code est dans mon esprit et je l'ai tapé ici, donc j'ai peut-être oublié quelque chose.
2) Le programme se terminera uniquement en étant tué (j'utilise linux), donc la méthode disposer de ClientManager ne sera pas explicitement appelée dans le programme principal (puisqu'il s'agit d'une classe statique). Encore une fois, si je dis quelque chose de mal s'il vous plaît dites-moi!
3) Désolé mon mauvais anglais :)

+1

Pourquoi vous souciez-vous de la fragmentation de la mémoire? Si vous avez seulement 100 clients, cela ne devrait pas être un gros problème. – thebretness

+0

Dupliquer? Même affiche: http://stackoverflow.com/questions/2320106/help-with-memory-allocation-for-multiplayer-game-server –

+0

Oui, cette question était aussi la mienne mais celle-ci concerne un pool de gestion de clients – Akira

Répondre

3

Couple de commentaires:

  • Utilisez un std :: vecteur pour tenir vos objets client, et non pas une sorte de structure Homegrown. Ça va vous simplifier la vie
  • Je pense que ça ira si get() renvoie une référence à condition que l'utilisateur sache que la référence peut être invalidée si elle y reste trop longtemps
  • exception dans get() si l'élément n'est pas là; c'est ce que sont les exceptions et il vous permettra de gérer l'élément manquant au niveau approprié

En ce qui concerne (2), vous pouvez gérer le signal approprié et appeler disposer(). Je pense que vous voulez probablement fermer les prises avant de sortir.

+0

Le problème avec renvoyer une référence est si l'argument d'entrée n'est pas valide. Je retournerais un pointeur, en utilisant NULL comme un cas d'erreur. Sinon, vous pourriez créer un client membre appelé nullClient et l'utiliser comme NULL sans risque de segmenter les erreurs (bien que vous ayez un risque plus élevé d'erreurs logiques non détectées). – thebretness

+0

A propos du vecteur, initialement je l'utilisais mais pour passer les données client entre les objets je devais passer un pointeur (comme indiqué dans http://stackoverflow.com/questions/2320106/help-with-memory- allocation-for-multiplayer-game-server). Donc, je suppose qu'un gestionnaire d'objets statique serait mieux – Akira

+0

Aussi, pouvez-vous me pointer un endroit où je peux apprendre à utiliser le signal que vous avez mentionné? – Akira

1

Faire beaucoup de l'état statique ne fait pas une «classe statique». C'est juste une classe comme les autres, avec des fonctions membres statiques. Vous n'avez pas vraiment besoin de le faire de toute façon: faites simplement de ClientManager une classe normale et créez-en une seule dans votre jeu.

Je conseille d'utiliser un vecteur std :: à la place de votre propre tableau.Cela rendra votre code plus robuste et vous facilitera la configuration des clients, en utilisant le bon constructeur pour chacun d'entre eux au lieu d'utiliser un constructeur par défaut pour le lot.

Vous ne devriez pas vous inquiéter de la fragmentation de la mémoire - c'est un détail de bas niveau tellement ésotérique que vous ne devriez même pas y penser. La chance d'être un problème pour vous est astronomiquement faible. Mais même si cela devait être un problème, nous ne pouvions pas le diagnostiquer en fonction de ce que vous avez posté ici, car nous ne savons pas ce qui constitue une classe Client. Il ne sert à rien d'avoir un pool soigneusement géré dans la classe ClientManager si la classe Client référence elle-même toutes sortes de mémoire ailleurs. À ce stade, vous devriez vous concentrer sur l'écriture d'un code robuste et correct et l'optimiser plus tard si nécessaire.

Vous pouvez aussi renvoyer un pointeur à partir de ClientManager :: get. Assurez-vous de vérifier si c'est null avant de l'utiliser. Vous pouvez entièrement supprimer le besoin de renvoyer des clients à partir de ClientManager si vous choisissez une interface différente, par exemple. passer l'opération do_something avec les données dans un membre de ClientManager qui recherchera le client et appellera l'opération si le client a été trouvé, ou signalera une erreur si ce n'est pas le cas. (Bien qu'il n'y ait pas de bonne raison de ne pas le trouver, si votre ClientManager est correct.)