Now, in the last important part of the description, the "glue code"
comes to aid in the task of making a sense out of it all.
Unfortunately, it is this glue code that I like less, as it is a
concentration of mixed responsabilities in just one object. But then,
this is the fastest way I found to make it work and still look "nice"
in the OO-way-of-life (I don't want any zealots throwing rocks on me
while I walk on the streets)
Those interfaces and classes in the last 2 sections defined how we
handled receiving events and sending data. That is, the "interface"
between our network code and the network.
What I will try to do now is to present the "interface" from the point
of view of our server internals and the network code. the place where
those many streams of bytes become objects used inside the program.
A few interfaces come handy in here:
// this interface is not exactly finished yet, but most of what is not
finished won't be needed in our code... was just planned to make the
classes "complete" outside the definition of Nutshell.
NetworkControl // this is an interface.
public:
virtual void connect(InetAddress &address, uint16 port) = 0;
virtual void listen( uint16 port ) = 0;
virtual void close( int32 ioChannel ) = 0;
// ... and several other functions that, usually, only wrap calls to
some underlying Socket object. These are just defined because we WILL
need the "close()" one, so the message processing system can create
the action of closing a connection and all its related data.
//The InetAddress class is just a basic class that wraps the
information about a "struct sockaddr_in" and provides some helpers for
gethostbyname() and stuff like this. I will define it when we get to
define the "Socket" class.
Along with this class, we have another interface, that should probably go here.
MessageStream // this is an interface.
public:
virtual bool hasMessages() = 0;
virtual Message * fetchMessage() = 0;
virtual void clearTarget( int32 clientId ) = 0;
Well, those are simple, at least the first two. The last one is a
helper method just to speed things a bit: It is there so that,
whenever we find out that a client is dead (the processing of the hang
up message, for instance) we can tell the queue to discard every
message from that client that is still in the queue... saving us the
trouble of getting the message, making a process out of it and
everything that will come to us later.
Now, for the ugly part of the play, we have the NetworkAdapter... it
is ugly even in the name. I couldn't find a good name for it, so I'm
up for ideas. What exactly does it do ? The following:
First it implements pretty much every interface we saw so far in the
network classes:
it is a MessageStream, an IoSelectorListener, a NetworkControl and a
DataSendSource.
... ... Didn't I Tell you it was ugly ? Yeah, I know it is horrible
but check this... doing it this way, we can have all "socket
information" (the receive buffers, the send queues, and the send
buffers ) insulated inside this one big object. which means that,
whenever we have a read error, and decide we have to disconnect a
client, we waste no time passing this information to every bit of code
that uses information about that client: we just generate some kind of
ClientReadError message to be processed (this will clean this clients
in-game information from our pool) and do the cleaning on the network
stuff : we can tell our IoSelector to unregister it from every event,
delete its sendqueues, clear all the messages we have from it in the
MessageQueue and close the socket.
Ok.. I know it won't justify, that it is still ugly and should not be
made this way... but.... if you give me better Ideas I would LOVE to
hear them... I tried for 3 weeks to make this work... and I am just
unable to do it... not good enough in OO I guess.
All I need is a fast, efficient, pretty, OOized version of this code
that works in the context of Nutshell... :)
all I got was a fast and efficient, ugly and OOWrapped version :)
I'll leave you digesting this... see ya,
Alexandre Moreira.
|