Question

Je suis la conception d'un serveur de jeu avec des capacités de script. La conception générale va comme ceci:

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 pour moi le plus difficile maintenant.

À l'heure actuelle 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 que quelque chose est reçu. Si la mémoire tampon est valide, le rappel est appelé où il est agir sur ce qui est dans la mémoire tampon. Une chose à noter est que les callbacks peuvent descendre au moteur de script, auquel point rien est sûr de ce qui peut arriver.

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

Qu'est-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 se enfin destructed et la mémoire libre. Cependant, je reste encore un peu de code en cours d'exécution après le rappel se termine dans le client, je ne peux donc pas la mémoire libre.

Que dois-je changer dans la conception? Dois-je avoir un événement chronométré dans le Server qui vérifie que Clients sont libres de détruire? Est-ce que créer des frais généraux supplémentaires Je ne veux pas? Serait-il toujours bien après le rappel se termine d'exécuter du code minimal sur la pile (return -1;) ou non?

Je ne sais pas quoi faire, mais je suis ouvert pour la conception complète remanie.

Merci d'avance.

Était-ce utile?

La solution

Vous pouvez utiliser un pointeur de référence compté comme boost::shared_ptr<> pour simplifier la gestion de la mémoire. Si la liste des clients du gestionnaire utilise shared_ptrs et le code qui appelle les callbacks crée une copie locale de la shared_ptr le rappel est appelé, l'objet restera en vie jusqu'à ce qu'il soit retiré du gestionnaire et la fonction de rappel est complète:

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. Ainsi, la création d'une copie locale du shared_ptr avant un appel de fonction fait que l'objet n'est pas supprimé de façon inattendue.

Autres conseils

Vous devriez envisager d'avoir un objet comme « Session » qui permettra de suivre les flux de message particulier du début à la fin (de 1 client). Cet objet doit également prendre soin de 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 un résultat d'événement prévu: le succès, l'échec, délai d'attente. Chacun de ces types devrait se refléter avec votre logique. En général, lorsque vous travaillez avec des événements, pensez à votre logique de traitement pour être un automate avec un état.

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

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

Mon idée générale est que le client :: disconnect () ne déconnecte pas immédiatement le client, mais seulement après que le rappel a terminé son exécution. Au lieu de cela, il affiche juste un événement au EventManager (ou serveur) classe.

On pourrait faire valoir que la méthode client :: disconnect () est sur la mauvaise classe. Peut-être qu'il devrait être serveur :: disconnect (client * c). Ce serait plus en ligne avec l'idée que le serveur est « propriétaire » du client et du serveur qui déconnecte les clients (et met à jour une comptabilité interne).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top