This is a fairly standard size for TCP/IP buffers. Other than that, it was
essentially arbitrary.
Messages - four arguments
Without using mallocs, it would have been overly painful to parse/decompose
a message in C into an arbitrary number of arguments. So I picked three.
At some point, three was inadequate, so I upped it to four. Then I fixed
that inadequacy with clientkeys, and now only need three for all my messages,
but left the other argument there "just in case".
Integers in Messages - not using network byte order
I thought it better to be explicit about my byte order in case I or someone
else wanted to write a server and the needed message/clientkey libraries in
a language that did not know what 'network byte order' was. This also left
me free to use only two bytes for message length and argument length (since
a message is bounded to 4096 bytes, this is sufficient).
Client Keys - maximum depth of ten
This is almost too high - no single server could support 240^10th clients.
But I did want to be able to chain multiplexers (presumably from different
locations) and decided that it would be better to be too high than too low. I
figure if you were chaining multiplexers ala IRC (though bound to a central
server instead of peer to peer), you would take other steps in the config to
make sure the overall client limit was kept down, in which case it might not
be unreasonable to chain more multiplexers than you could support the maximum
capacity of.
Internal Communications - sockets instead of pipes
I wanted to be able to run the multiplexer and the server on different
machines. Theoretically, it is even possible to run the listeners on different
machines if you diddle some shell settings and change the command from
"./tcplistener" to something like "ssh foo@bar tcplistener".
Client Keys - ints instead of bytes
A listener which logs into a server like AIM or ICQ has no reason to be
limited to the (usual) file descriptor limit of 255 to index its
clients. Presumably, it would only use one port for the multiplexer, and one
for the connection to the other chat system, and actually use client key
numbers to refer to entries in its list of users it is talking to/watching
for. As for that first entry (the depth), which is an int instead of a byte
despite having a maximum value of ten, I was busily being happy that I could
reuse the int code/decode routines to keep the same black-boxing of integer
byte order, and didn't worry too much about the three byte waste there. This
might be 'fixed' later.
Multiplexer - C instead of (your favorite language here)
Since the multiplexer is the bottleneck of the system, I figured it should be
as egregiously over-efficient as possible. No language that garbage collects
at random intervals, inserts array bounds checks even where you know you won't
go outside, arbitrarily puts semaphore checking in data structures even when
used in a non-threading environment, et cetera is ever going to be as
efficient as a slimmed down C program. There's a reason Apache is in C. The
same reason applies to AgNet.
Message IDs - gaps in the list of numbers
Unless you try to 'enum' them, there's no reason NOT to have gaps, and my
original intent to was to keep related messages together. So I figured I'd "go
to the next multiple of ten" when I added messages of a new relation-group.
Since then, additions to the message list have squeezed some of the gaps
around, and so it no longer looks so organized. And there's no message ID
zero because that used to be the error return from parsing a message (before
I rewrote the parser under the rememberance that I wouldn't get one message
per read() just because the write()s were that way).
Build Environment - make instead of Ant
You probably have make. You probably have to go get Ant. Plus I would have to
learn how to make Ant work. The builder might be changed to use Ant in the
future, anyway. This is just the alpha release.