Jump to content
  • Advertisement
  • entries
    109
  • comments
    175
  • views
    117652

Message handler management

Sign in to follow this  
Emmanuel Deloget

782 views

In a previous entry, I explained how to use pointers to member functions in the design of a windowing system.Today, I applied this to another problem: network messaging.
In client-server mode, both the client and the server are sending messages to the other. It appeared to me that it was a good candidate for using a message map, and, of course, the corresponding message handlers. I'll explain everything from the server perspective - but the client is working the same way, and reuse the same code.
  • the client sends a message.
  • the server read the message from the client.Once he knows that the message is complete, he decodes it using a MessageCodec.
  • since he now know what is the exact message, he uses a handler manager that is similar to the one I already presented in order to hook to the right message handler.

The system is fairly simple, and it took only one hour to add it to my base code. Here is a small UML class diagram to show the different base classes:



Let's go and use an example: the client wants to log in the server. We defines the LoginMessageCodec:

class LoginMessageCodec : public MessageCodec
{
std::string mLogin;
std::string mPassword;

public:
LoginMessageCodec();
virtual ~LoginMessageCodec();

bool encode(std::vector<char>& buffer);
bool decode(std::vector<char>& buffer);

MessageCodec *clone();

void setLogin(const std::string& login) { mLogin = login; }
void setPassword(const std::string& passwd) { mPassword = passwd; }
const std::string& login() const { return mLogin; }
const std::string& password() const { return mPassword; }
};


The pseudo code from the client point of view is:

LoginMessageCodec login;
std::vector buffer;

login.setLogin("aaaaaa");
login.setPassword("bbbbb");
login.encode(buffer);
clientsocket.send(buffer);

From the server point of view, the pseudo-code is

LoginMessageCodec login;
std::vector buffer;

serversocket.recv(buffer);
login.decode(buffer);
mMessageDispatcher.dispatch(login, sender);

The message dispatcher pseudo-code is:

for each handler {
if (handler can handle the message) {
handler->call(message, sender);
}
}

The handler inherits from the MessageHandler class. This handler has an associated pointer to member and simply calls the member function. In the case of the login mesasge, I defined the LoginMessageHandler:

template <class ReceiverType, class SenderType> class LoginMessageHandler
: public MessageHandler
{
public:
typedef void (ReceiverType::*LoginMethod)(SenderType sender,
const std::string& login,
const std::string& password);

private:
ReceiverType *mReceiver;
LoginMethod mMethod;

public:
LoginMessageHandler(ReceiverType *receiver, LoginMethod method)
: MessageDispatcherHandler(MessageCodecCodes::login),
mReceiver(receiver), mMethod(method)
{
}
virtual ~LoginMessageHandler() { }

virtual void call(MessageCodec *codec, SenderType sender)
{
LoginMessageCodec *logincodec;
logincodec = dynamic_cast(codec);
if (logincodec) {
((*mReceiver).*(mMethod))(sender, logincodec->login(), logincodec->password());
}
}
};

See the side note below for more informations about this dynamic_cast<> call. I created the login message handler instance using this call:

void Application::onLogin(ServerSocket& socket, const std::string& login, const std::string& password)
{
// verify user/password
}

void Application::initHandlers()
{
mMessageDispatcher.registerHandler(
new LoginMessageHandler(
this,
&Application::&onLogin));
}

dynamic_cast<>
In our context, dynamic_cast<> can be used safely. It doesn't break the OCP [RM-1], nor it breaks the LSP [RM-2]. From a design point of view the login mesasge handler is meant to use a login message codec.
The real magic of everything is not about the dispatching of the messages to member functions. It is about doing it safely, and having everything which is verified by the compiler. At the expense of using MessageHandler derived classes - which are rather simple tools - I implemented a reasonably secure, type-safe message handler system that is similar to the highly dangerous MFC message marshaler (seriously, have a look to the MFC message dispatcher (it is not difficult: create a message handler, adds a breakpoint, then step out the function and have a look to the infamous beast)). By defining the MessageDispatcher as a template, I also managed to create a highly reusable system.

Voila!

Any question or comment?
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!