views:

91

answers:

3

I have client/server applications and a very simple protocol for communication. More precisely, it's a set of commands a server sends and a set of requests a client can make.

The idea is as follows:

When a command is made by the server, a client has to execute it. When a request is made, server checks permissions and if everything is ok it grants the request.

The application is written in C++ and I got slightly stuck designing the architecture for this sort of communication protocol. Assuming that command/request is a tab-delimited string with first parameter being the name of the message, I was planning to make a MessageManager class which would store all messages in a hash-table and retrieve them when necessary. The problem here is this:

typedef std::vector< std::string > ArgArray;
class Request : public Message
{
public:
   Request( const char *name ) : Message( name ) { }
#ifdef CLIENT
   /** problem here **/
   virtual void make( ... ) = 0;
#elif defined SERVER
   virtual void grant( const Client &c, const ArgArray &params ) const = 0;
protected:
   virtual void checkPermissions( const Client &c, const ArgArray &params ) const = 0;
#endif
};

Because different messages can take different arguments to be constructed I can't really create a complete interface. For example, some messages might need a simple string to be constructed, whereas others might need some numeric data. This complicates things and makes the design a bit untidy... I.e. I have to get round the problem by ommitting make() from interface definition and simply add different make() for each Request I make. Also, if I wish to store pointers to different Requests in one container I cannot use dynamic_cast because Request is not a polymorphic type. An obvious (untidy) solution would use make( int n, ... ) definition and use stdarg.h to extract different arguments but I consider this to be unsafe and confusing for the programmer.

There is obviously a design flaw in my idea. I already have a solution in mind but I am just wondering, how would people at SO tackle this problem? What sort of Object architecture would you use? Is there a simpler approach that could solve this problem? There aren't any specific requirements to this except to keep it as simple as possible and to keep the actual protocol as it is (tab-delimited strings with first parameter indicating which message it is).

+1  A: 

I think the problem in your design is that you tried to cram too many functionality into one class. For example, the part about constructing/parsing messages to contain numeric data or string (i.e. serialization) should be separate from the underlying connection logic.

Check out Boost.Serialization if you are allowed to use additional libraries. Boost has got a very nice network library too called ASIO. Even if you are not allowed to use boost you should probably consult their library design.

oykuo
That is kind of the solution I was having in mind. Separate the actual parsing from execution. I.e. have a serializable structure for each message which gets created and the gets passed on further on. We can then make virtual function `make()` which takes no arguments because they are all members of a structure. :)
sneg
Sorry. When I wrote `make()` I made a mistake. I meant an executing function which carries out request/command.
sneg
another thing to consider is to use Visitor's pattern for your parsing and serializing code. It worked great when you have a predefined set of message classes that you need to handle.
oykuo
+1  A: 

Yes, you say that "command/request is a tab-delimited string": so as kuoson said, it's isn't true that "different messages can take different arguments" ... instead, all messages are being constructed from the tab-delimited string.

ChrisW
What I meant is that when you create a message you might need to serialise some data. I.e. if I follow the current implementation I might have something like: `make( int x, int y, int z )` which will create a message and send over 3 integers. The receiver then gets the tab-delimited string. :)
sneg