Though you don't need this to write a server based on one of the baseservers, and you can crib code or use a library, anyway, here is the message specification. This might be useful if you need to write/debug one of the message libraries or write a listener without consulting existing code. Actually, using the message spec may not be an entirely bad idea for writing a server that uses a client (of your authoring, presumably).
A message contains as few as four and as many as 4096 bytes. And from zero to four arguments. These limitations are admittedly aribitrary, and exist mostly because I didn't want to make the C version do a bunch of malloc()s. They are 'supposed' to be efficient to build, transfer, and parse.
The first 4 bytes are the mandatory header of the message. The first two specify the length. The third is the message 'id' (0 - 256), and the fourth is the numbe of arguments (0 - 4. I know I could have just used 2 bits for that, but it's a pain to write bit masking code and leads to less efficient data access). Anyway, the message numbers actually used in multiplexer->listener communication and multiplexer->server communication are defined in header files and the symbolic names should always be used for message IDs. Naturally, you'll want to make your own header file for messages which you might use to go directly to a client (then you'll be using a message-in-a-message, but it should work).
Any arguments consist of 2 bytes for the length, and then exactly that many bytes for the content (strings are NOT null terminated in the message and a parser for languages which rely on null termination must take care of that where appropriate). Currently, there are just 3 argument types supported: 32 bit integers, client keys, and arbitrary binary data. The Perl message parser I wrote will choose to interpret "arbitrary binary data" as a "string" if and only if the contents of the data contain only ASCII codes 20-126 and/or newlines, carriage returns, and/or tabs (ASCII 10, 13, 9).
The length arguments at the beginning of the message and the beginning of each argument all work exactly the same way. The first byte is the high-order bits, and the second byte is the low order bits. Since the maximum message length is 4096, the first byte will always be 16 or less, so it wastes a few bits itself, but such is life.
An "integer" message argument is always a 32 bit integer, and always the bytes go from highest to lowest (big endian). This may or may not correspond to network byte ordering, but I didn't care, because I picked one and stick to it (both in the length fields and the integer arguments). Specifically, if the bytes in the message are '01 00 00 00', the number is really big, and if they are '00 00 00 01', the number is 1. Note that _time_ in C (on my machine) is a 32 bit integral number of seconds, and so if the argument is supposed to represent time, you may need to tweak it (for example, if your language uses time in milliseconds instead of seconds) (times are also timezone independent in C, so don't worry about that until you go to display the time, then convert it to the timezone you prefer for your users).
A binary data argument is just that. This is mostly useful for entering strings such as user input. Remember that they will not be null-terminated when extracted from the message (for languages were that matters). It is a "bad idea" to pass C structs around as binary arguments - you should either define a new argument type for the struct, or else pass the relevant members of the struct as arguments unto themselves.
A client key argument consists of from two to eleven integers, and implementations should use the integer parser needed for integral arguments. More about clientkeys in a forthcoming document, but suffice it to say that the first int is the number of 'keys' (distance from server), and the remaining ints are that many keys, with the client number on the closest listener (from the server's point of view, this is always the multiplexer) on the end. Thus the client key "00 00 00 02 00 00 00 05 00 00 00 01" would (assuming this is on the server) go to the client #1 of the multiplexer (i.e. listener 1) and to client #5 on the listener. Thus a client key on a listener is always 2 ints (remember one is for number of ints following), and the client key on the multiplexer is always at least 3 (multiplexer->listener). Actually, in the simplest set up, the client keys on the server are _always_ 3 ints. The extra space allocated is so that multiplexers can be defined to treat other multiplexers as 'listeners' in the future.
|message #5 with no arguments.|
|message #23 with a client key of 199:199 as the first and only argument.|
|message #47 with the number 11 as the first argument and the string "hello, world" as the second.|
One final note: because the maximum message size is 4096, there is a maximum 'chunk' of user input which can be passed to the server all at once (and vice versa). Since all communication of user input to and from the server uses messages requiring 2 arguments, a client key and the text, and the cliet key can have a 'depth' of 10, we can compute the maximum chunk of user text. 4096 minus 4 (for message overhead) minus 2 (for length of client key) minus 44 (for max size of client key) minus 2 (for length of text), or 4044 bytes. Though nothing prevents from sending 2 messages in a row, if you have a whole LOT of user text to send to the user, you should break it up even more and send it over time (like make a queue of outgoing messages and send 2 per 'tick' or something). On the other hand, AgNet is designed for rapid back-and-forth communications, and should not be used as a file server.
Doc Index / Message Implementations
|This project is hosted by sourceforge.net:||project homepage||silver's homepage|