2009-09-21 9 views
0

Je suis en train de concevoir un serveur de jeu avec des capacités de script. La conception générale va comme ceci:Conception client/serveur pilotée par événement avec C++

Client connects to Server, 
Server initializes Client, 
Server sends Client to EventManager (separate thread, uses libevent), 
EventManager receives receive Event from Client socket, 
Client manages what it received via callbacks. 

Maintenant, la dernière partie est ce qui est le plus difficile pour moi maintenant.

Actuellement mon design me permet d'une classe qui hérite Client pour créer callbacks à des événements spécifiques reçus. Ces rappels sont gérés dans une liste et le tampon reçu passe par un processus d'analyse chaque fois qu'un élément est reçu. Si le tampon est valide, le rappel est appelé là où il agit sur ce qui est dans le tampon. Une chose à noter est que les rappels peuvent descendre au moteur de script, à quel point rien n'est sûr de ce qui peut arriver.

Chaque fois qu'un rappel se termine, le courant tampon de réception doit être remis à zéro, etc. ont actuellement aucune Callbacks capacité de retourner une valeur, car comme indiqué précédemment, tout peut arriver.

Ce qui se passe est que lorsque quelque part dans quelque chose de rappel dit this-> disconnect(), je veux déconnecter immédiatement le Client, retirez-le de la EventManager, et enfin retirer de la Server, où il devrait également obtenir enfin mémoire destruite et libre. Cependant, j'ai encore du Code en cours d'exécution après la fin du rappel dans le Client, donc je ne peux pas libérer de mémoire.

Que devrais-je changer dans la conception? Dois-je avoir un événement chronométré dans le Server qui vérifie quels Client s sont libres de détruire? Cela créerait-il des frais supplémentaires dont je n'ai pas besoin? Serait-il toujours correct après la fin du rappel pour exécuter du code minimal sur la pile (return -1;) ou non?

Je n'ai aucune idée de ce qu'il faut faire, mais je suis ouvert pour des révisions de conception complètes.

Merci d'avance.

Répondre

1

Vous pouvez utiliser une référence compté comme pointeur boost::shared_ptr<> pour simplifier la gestion de la mémoire. Si la liste des clients du manager utilise shared_ptr s et que le code appelant les rappels crée une copie locale du shared_ptr le rappel est appelé, l'objet restera actif jusqu'à ce qu'il soit supprimé du gestionnaire et la fonction de rappel est terminée:

class EventManager { 
    std::vector< boost::shared_ptr<Client> > clients; 

    void handle_event(Event &event) { 
    // local |handler| pointer keeps object alive until end of function, even 
    // if it removes itselfe from |clients| 
    boost::shared_ptr<Client> handler = ...; 
    handler->process(event); 
    } 
}; 

class Client { 
    void process(Event &event) { 
    manager->disconnect(this); 
    // the caller still holds a reference, so the object lives on 
    } 
} 

l'objet Client sera automatiquement supprimé une fois la dernière shared_ptr à elle est hors de portée, mais pas avant. Donc, en créant une copie locale du shared_ptr avant un appel de fonction, assurez-vous que l'objet n'est pas supprimé de façon inattendue.

+0

Cela nécessitera le moins de changements du système actuel, donc j'opte pour cela. Je vais toujours considérer les suggestions des autres. Merci! – Chaosteil

0

Laissez la fonction Client :: disconnect() envoyer un événement à la classe EventManager (ou Server). Cela signifie que vous avez besoin d'une sorte de gestion des événements dans EventManager (ou Server), une boucle d'événement par exemple.

Mon idée générale est que Client :: disconnect() ne déconnecte pas le client immédiatement, mais seulement après l'exécution du callback. Au lieu de cela, il publie simplement un événement dans la classe EventManager (ou Server).

On pourrait argumenter que la méthode Client :: disconnect() est sur la mauvaise classe. Peut-être que cela devrait être Server :: disconnect (Client * c). Ce serait plus en accord avec l'idée que le serveur «possède» le client et c'est le serveur qui déconnecte les clients (et met à jour une certaine comptabilité interne).

1

Vous devriez envisager d'avoir un objet comme "Session" qui suivra le flux de messages particulier du début à la fin (à partir de 1 client). Cet objet doit également prendre en charge l'état actuel: principalement les tampons et le traitement. Chaque événement qui déclenche un rappel DOIT mettre à jour l'état de la session correspondante. Libevent est capable de vous fournir tout résultat d'un événement planifié: succès, échec, timeout. Chacun de ces types devrait être reflété avec votre logique. En général, lorsque vous travaillez avec des événements, considérez votre logique de traitement comme un automate avec un état.

http://en.wikipedia.org/wiki/Reactor_pattern peut être une bonne ressource pour votre tâche.

Questions connexes