Crossbar Server, Portable Messaging and Support Classes

6/26/2001 v1.80 jaf@lcsaudio.com

Jeremy Friesner/Level Control Systems

  • Note: To compile the server, cd to the "server" subdirectory and type 'make'. The server executable, "muscled", should be generated. You can run this server from the command line, (type "muscled help" to get info about some options) and connect to it with any MUSCLE-aware program. (The primary publicly available MUSCLE program that I'm aware of is BeShare, which can be found at http://www.bebits.com/app/1330. There is also a networked battleship game that uses MUSCLE, and a few other things floating around). The testreflectclient program in the tests directory is good for experimenting, too.

    The main goal of these classes is to provide an easy way to use BMessage-style message passing across a network of heterogeneous (BeOS, Linux, Windows, etc) systems. The secondary goal is just to provide some handy data containers and other utility classes. All of this code (except the atheossupport and besupport directories) should compile under any up-to-date C++ compiler--no proprietary APIs are used.

    For better documentation than this, please see the MUSCLE web site.

    This code has been compiled (using gmake) and tested in the following environments:

    1. BeOS R5/x86 on single and Dual Celeron 500's
    2. BeOS R5/x86 with BONE on single and Dual Celeron 500's
    3. BeOS 4.5.2/x86 on single and Dual Celeron 500's, and on a Dual Pentium II 400
    4. BeOS 4.5.2/PPC on a PowerMac 4400/200
    5. Red Hat Linux 6.1/x86 on single and Dual Celeron 500's
    6. LinuxPPC/2000 on a Motorola Starmax 3000/180
    7. FreeBSD 4.0 on an Intel PC
    8. MacOS/X on a G3 Powerbook
    9. AtheOS 0.3.4 on an Intel PC (of some sort)
    10. Microsoft Windows, using the Cygwin Unix-adaptation library.

    It has no known bugs, but may have some yet-to-be-discovered ones. Use at your own risk, LCS is not responsible for any disasters, blah blah blah.

    Directory contents descriptions follow:

    1. atheossupport/

      AtheOS port of the code in the besupport folder. (See below)

    2. besupport/

      This directory contains two handy functions. One converts a BMessage into a PortableMessage, and the other converts a PortableMessage back into a BMessage. These functions allow your BeOS-specific code to use BMessages internally, and only convert to PortableMessages when it's time to send them over the disk or the network. Since this code accesses the BMessage class, it will only compile on a BeOS system.

      This directory also contains the MessageTransceiver class, which is a useful way to send and receive PortableMessages over a TCP socket without having to worry about blocking issues (or messing around with select()). You can just create a MessageTransceiver, and call SendOutgoingMessage() whenever you want to send something over the socket. Conversely, whenever one or more PortableMessages is received over the socket, a PORTABLE_MESSAGES_RECEIVED message is sent via your BMessenger, so your BLooper knows to process them. This class also contains convenience methods for asynchronously connecting to a remote server or accepting one or more incoming connections.

    3. dataio/

      This directory contains the PortableDataIO class, which is similar to Be's BDataIO class. It also includes TCPSocketDataIO, FileDataIO, MemoryBufferIO, and NullDataIO; these are subclasses for reading/writing data to TCP sockets, disk files, in-memory arrays, and the bit-bucket, respectively.

    4. iogateway/

      This directory contains the AbstractMessageIOGateway interface and the PortableMessageIOGateway class, which is a "gateway" object that knows how to manage bidirectional PortableMessage-stream traffic to and from a PortableDataIO stream. It queues outgoing PortableMessages and sends them when it can, and also receives incoming PortableMessages and queues them for pickup by the user's code.

    5. message/

      This directory contains the PortableMessage class. PortableMessage is very similar to Be's BMessage class, although it has been trimmed down a bit. Here are some relevant details:

      1. PortableMessage supports the following field types:
        1. int8 (B_INT8_TYPE)
        2. int16 (B_INT16_TYPE)
        3. int32 (B_INT32_TYPE)
        4. int64 (B_INT64_TYPE)
        5. bool (B_BOOL_TYPE)
        6. float (B_FLOAT_TYPE)
        7. pointer (B_POINTER_TYPE)
        8. message (B_MESSAGE_TYPE)
        9. PortableFlattenable (B_OBJECT_TYPE)
        10. PortableString (B_STRING_TYPE)
        11. PortableRect (B_RECT_TYPE)
        12. PortablePoint (B_POINT_TYPE)
        13. raw data buffer (B_RAW_TYPE)
        14. user-define raw data buffers or flattened objects (any other typecode)
        Certain other types that BMessage supports, such as Specifier, BMessenger, and entry_ref, are not (directly) supported by the PortableMessage class.
      2. The PortableMessage class is only a data container. Unlike the BMessage class, it is not tied in to any messaging or threading system (although nothing prevents you from using it in conjunction with one, if you want to)
      3. PortableMessage is a subclass of PortableFlattenable (see below), and as such can be serialized out to a flat buffer-of-bytes. The format of this flat buffer is NOT compatible with the format of the serialized buffers produced by the BMessage::Flatten(). So you won't be able to flatten a BMessage and unflatten it as a PortableMessage. Instead, to get cross-platform portability you should convert your BMessages to PortableMessages in memory (using ConvertBMessageToPortableMessage()), and flatten only the PortableMessages.
      4. PortableMessage methods that return a status_t only return B_ERROR or B_NO_ERROR. Other error codes (such as B_BAD_INDEX, etc) are not returned.
      5. Some API calls have been modified (enhanced?) slightly.
        1. GetInfo() now returns field names in a PortableString object rather than by setting a (const char *).
        2. All occurrences of ssize_t have been replaced with size_t.
        3. All obsolete methods are gone.
        4. All Add*() methods have a Prepend*() counterpart, which can be used to efficiently prepend items to the beginning of the field's item array.
        5. AddData() can take a NULL data pointer, which will cause it to add default data items.
      6. PortableMessage has a GetFieldNameIterator() method, which returns a PortableMessageFieldNameIterator object, which can be used to iterate over the fields of a PortableMessage. BMessage-style GetInfo() based field iteration works too, but isn't as efficient.
      7. PortableMessage has MoveName() and CopyName() methods, for efficient copying or moving of message entry fields from one PortableMessage to another.
    6. reflector/

      This directory contains server code for an n-way "message crossbar server" program. This program will listen on a specified port for TCP connections, and will allow the TCP connections to "talk to each other" by exchanging PortableMessages. The ServerProcessLoop() method implements the server, while the AbstractReflectSession class is the interface for the server's side of a TCP connection. There are currently two subclasses of AbstractReflectSession included: the DumbReflectSession class just reflects all messages to all connected clients, while the StorageReflectSession class adds nice features like wildcard-based message routing, server-side data storage, and "notify-me-on-change" subscription services. (See StorageReflectConstants.h or the MUSCLE Beginner's Guide for full documentation) Other protocols can be defined by creating new subclasses of AbstractReflectSession.

    7. server/

      This contains the Makefile and main entry point for the "muscled" server executable, and the "admin" server-tweaker utility.

    8. support/

      This directory contains various "small things" needed to compile the rest of the code. These include byte-ordering macros, BeOS-style type codes, typedefs, and result constants, and the PortableFlattenable interface definition.

    9. syslog/

      This directory contains functions for logging event messages to stdout and/or to a file. Log messages can be "raw" (works just like printf) or nicely formatted with the current time and so on.

    10. test/

      This directory contains several silly little test programs that I used while developing the code, and a Makefile to build them with.

    11. util/

      This directory contains many useful one-off classes and function collections, including:

      1. PortableHashtable

        PortableHashtable was originally written by Michael Olivero, and then modified by myself. PortableHashtable was inspired by the java.lang.Hashtable class, and works very similarly to that. PortableHashtable is used by the PortableMessage class, but is also useful in its own right.

      2. NetworkUtilityFunctions

        NetworkUtilityFunctions.cpp is a repository for common BSD socket operations (like setting up sockets to connect or accept connections) that I'm tired of writing over and over again.

      3. TimeUtilityFunctions

        TimeUtilityFunctions.h is a repository of functions for dealing with microsecond-accurate timing issues.

      4. ObjectPool

        The ObjectPool class is used to avoid excessive deletions and allocations of commonly used objects (such as PortableMessages or RefCountMems). It works by recycling the items for re-use, and is templated so it can be used for any type of object.

      5. PortableQueue

        The PortableQueue class is just a little templatized double-ended queue (i.e. AddHead(), AddTail(), RemoveHead(), and RemoveTail() are O(1) operations). It can be used as a Vector, Stack, or FIFO. It's templatized for easy, type-safe reuse.

      6. RefCount

        The RefCount class implements generic reference counting for C++ objects or arrays. To enable reference counting on an object, you simply create a single PortableRef for that object, and (optionally) make one or more copies of the PortableRef via the copy constructor or the equals operator. Then, when the last PortableRef object disappears, the C++ object or array is automatically deleted. It's not a garbage collector, but it beats having to keep track of all your allocations by hand...

      7. PortableString

        The PortableString class is just your basic character-string class, in this case inspired by the java.lang.String class from Java. This class was originally written by Michael Olivero (mike95@mike95.com) and modified by myself. PortableString extends PortableFlattenable, so it can be serialized in a generic manner.

      8. StringTokenizer

        A string tokenizing class similar to Java's Java.util.StringTokenizer, only more efficient.

    For more details, have a look at the autodocs, header files and/or the source itself.

    -Jeremy