Hello all,

I am coding a client/server in C++ - everything is working so far but I was wondering if my approach is correct or if there is a better way of handling this:

In this version only 64 clients need to be able to connect, in a different application I will write in the future there really isn't a set limit, so please comment if you think my approach will cause serious problems when the number of clients grows.

The application consists of a few classes:
CListenServer - Listen for new connections and accept them.
CClient - contains functions like 'receive' and 'send'.

CListenServer uses select() to check if a new connection is available and accepts it if there is, also it checks all the connected client sockets to see if there is any data waiting for recv().

In the main code I keep a map that contains the socket number as key and a pointer to the client object as value (map<int, CClient*>).

So when CListenServer returns a list of sockets that have data waiting, I can find the correct client object by using map.find(socket) and call the recv function on this CClient.

In the CClient I store the following info of the client:
Socket
IP
GUID (the clients are connected to a game server aswell, the GUID is a unique identifier for each client that is written in the game server's logfile when a client connects - I read this logfile to get the GUID and IP).

So after reading the gameserver's log I have a clientIP and a GUID, I want to assign the GUID to the client object that is connected with 'clientIP'.
I did this with another map(map<string, CClient*>) the string here is the client IP, so I can find the client with map.find(clientIP) and call 'setGUID(GUID)' on this object.

There is also a map with the slot number and the client object, I also get the slot number from the game server's log.

So that is 3 maps already, and later I might need more(like a map between guid and object). Is there a better way to handle this? I read something about hash tables, but I wonder if it is a big improvement when there are just 64 clients(most of the time less anyway).

I hope someone can comment on this and possibly suggest a better way to deal with finding the correct object. Let me know if you need more information.

Chris

If you're worried about maps, don't worry at all. They will scale just fine. If you ever finding yourself wanting to eke out that last bit of performance, it should be easy to plug in various other implementations like hash tables or such. Consider using typedefs so that you would only have to make that change in one place.

What I'm more worried about is that I need a new map for every identifier to find the correct client object.
So atm there are 3 maps that all have 'CClient*' as a value but different keys(string, int etc.). There is no way to have this in 1 container?

Basically it woul be nice to have a structure as a key and that I can use 'map.find' and it will search the structure members for a match but I guess that is not possible or am I wrong?

What I'm more worried about is that I need a new map for every identifier to find the correct client object.
So atm there are 3 maps that all have 'CClient*' as a value but different keys(string, int etc.). There is no way to have this in 1 container?

Well, yeah, there is:

class Foo {
    map<string, CClient*> byThing_;
    map<int, CClient*> bySocketId_;
    map<blah, CClient*> byBlah_;

    void addCClient(CClient* c) {
        // this is me completely forgetting my STL, .add is probably wrong
        byString_.add(c->getThing(), c);
        bySocketId_.add(c->getSocketId(), c);
        byBlah_.add(c->getBlah(), c);
    }
    CClient* getBySocketId(int socketId) {
        return bySocketId_.find(int);  // or whatever it is; i forget STL
    }
};

Now you have all your maps safely synchronized; adding methods for looking up values is left as an exercise to the reader.

Basically it woul be nice to have a structure as a key and that I can use 'map.find' and it will search the structure members for a match but I guess that is not possible or am I wrong?

You would then either need three versions of 'find' (for each type of key) or some type that can represent any given type of key. I recommend going with what I described above.

Thank you for help, I have included your implementation now and it makes a lot more sense ;).
An extra advantage is that I can now include thread safety a lot easier since all adding/reading happens in one place.

After the select() returns the sockets that have data waiting I run the 'recv' function in a thread, the data is also processed here so based on the data from recv it might be necessary to disconnect that client(== an action on one of the maps). And other threads will require actions on the maps aswell(like a list of all connected clients etc.).

Anyway I've implemented that now aswell and I'm quite happy with the result, thanks again ;)

This article has been dead for over six months. Start a new discussion instead.