GT 2.0: The Groupware Toolkit

The GT library is a groupware toolkit with two goals: to simplify the development of real-time distributed groupware, and to improve the performance of distributed applications. The toolkit makes it simple to build groupware by taking care of networking and by providing several high-level programming abstractions to deal with communication and distributed data. It is designed to speed the development of simple groupware applications by allowing developers to create distributed systems without the need to program lower-level networking code.

This distribution includes the GT source code, prebuilt binaries, and several demonstration programs. The demonstration clients use a simple broadcasting server called the ClientRepeater. Please ensure the ClientRepeater is started before running any of the examples!

GT is made available under the GNU Lesser General Public License (LGPL) v2.1 or later. A copy of this license is included in the distribution as LICENSE.txt.

Requirements

Programming Model

GT maintains a strong separation of clients and servers. Although peer-to-peer systems can be implemented by having a node maintain both a client and a server, this has not been GT's focus.

GT exports a notion of communicating across a set of typed channels. Channels are typed to simplify sending structured information, such as strings, bytes, objects, or session messages. There may be up to 255 different channels of each type. Each bit of structured information is sent across as a message.

GT is designed to be non-blocking and, ideally, single-threaded. Applications are required to periodically poll by calling the Update() method on their GT interface (e.g., Client.Update() or Server.Update()). Events are signalled through .NET events. Applications are responsible for implementing equivalent blocking semantics when required.

Architectural Overview

GT is structured as a three-level architecture. At the top level is the application level API, represented as instances of GT.Net.Client and GT.Net.Server. Clients and servers export different interfaces for handling messages. These interfaces are nominally connectionless. At the middle level are connexions and marshallers. Connexions serve as an aggregation of the different resources that can be used to communicate with a particular endpoint; connexions are represented as instances of IConnexion. Clients and servers use marshallers to convert messages into a form suitable to be communicated across a network; marshallers are represented as instances of GT.Net.IMarshaller. At the lowest level are transports which serve to communicate bundles of bytes to an endpoint using some network protocol; these transports are represented as instances of ITransport.

GT supports communicating across a variety of networking mechanisms, such as TCP and UDP. Applications can provide minimum delivery requirements for particular channels and for individual messages, such as whether a message must be sent across a networking mechanism with guaranteed delivery, or particular ordering requirements. These requirements are used to select a particular transport instance that meet those requirements.

Both clients and servers are built using a configuration object, GT.Net.ClientConfiguration for the client and GT.Net.ServerConfiguration for the server. Sample configurations are provided in GT.Net.DefaultClientConfiguration and GT.Net.DefaultServerConfiguration; these configurations are intended to serve only as examples and may change between releases. The examples should be copied and adapted for your particular application needs.

The GT Client

A client encapsulates the communication to different servers. Clients export a channel as a typed stream. We currently support 4 types of streams: strings (IStringStream), binary arrays (IBinaryStream), objects (IObjectStream), and session events (ISessionStream). GT also supports tuple streams, which provide automatically updating structures.

The main interface to GT Client is through instances of the GT.Net.Client class and the various stream instances.

Channels have quality-of-services requirements, described using a ChannelDeliveryRequirements object. These QoS requirements are used to find an appropriate transport able to meet those requirements. These QoS requirements may be overridden on a per-message basis using a MessageDeliveryRequirements object. Care must be taken for requirements specifying an aggregation level of MessageAggregation.Aggregatable: the user is responsible for periodically flushing the channel manually. These messages will be flushed by the periodic client/server ping, though this is usually scheduled to occur once every 10 seconds

The GT Server

Servers provide a more event-driven interface. As servers must scale to multiple clients, they are generally required to be more performant.

GT Marshallers

Marshallers have the responsibility of breaking object graphs into bytes, forms that can be transported across a network connection or written and read from disk. As multiple messages may be bundled together into a transport-level packet, a marshaller is responsible for placing message boundaries. This typically means that the marshaller should either write an explicit end-of-message indicator or tack a prefix to the content with a message length.

GT provides a standard object marshaller that uses the .NET Serialization. There are two variants of the marshaller. The first variant is a lightweight marshaller (LightweightDotNetSerializingMarshaller) that unmarshals only system-level messages, and leaves all application messages received as uninterpreted bytes; this is helpful for servers such as the ClientRepeater by avoiding any unnecessary latency by dropping all but the most basic functionality. The heavier-weight marshaller (DotNetSerializingMarshaller) uses the .NET serialization facilities.

We have also provided a proof-of-concept implementation of the general message compressor, an adaptively compressing marshaller that is well suited to marshalling data with repetitive structure between messages. This scheme is described in:

C Gutwin, C Fedak, M Watson, J Dyck, T Bell (2006). Improving network efficiency in real-time groupware with general message compression. In Proc of the Conf on Computer Supported Cooperative Work (CSCW), 119--128. (doi:10.1145/1180875.1180894, pdf)

Although GMC is fully functional, it is currently only supported across reliable and ordered transports.

GT Marshallers should never produce 0-byte messages. Marshallers should throw a GT.Net.MarshallingException exception on error.

GT Transports

Transports are responsible for carrying a set of bytes, called a Packet, to some remote endpoint. Transports describe their transportation characteristics which are used to match against a channel or message's required QoS requirements.

GT Transports should throw a GT.Net.TransportError when requested to send a 0-byte message.

Communicating Errors and Exceptions

GT uses both exceptions and an error event mechanism to communicate errors as they occur. There are several situations where errors may occur:

GT will raise exceptions on API violations or truly exceptional occurrences. Otherwise exceptions and errors are reported using the error event mechanism.

The exceptions currently defined are:

System.ArgumentException, System.ArgumentNullException
Thrown with invalid arguments
GT.ContractViolation
Thrown when documented conventions/contracts/constraints are violated; these are situations where there aren't particular problems with arguments to a method
GT.InvalidStateException
Thrown when a component is used when in an invalid state (i.e., it has been stopped or disposed)
GT.Net.MarshallingException
Thrown when an error occurs during marshalling / unmarshalling
GT.Net.TransportError
Thrown when an error has occurred when communicating across a particular transport
GT.Net.CannotConnectException
Thrown when there was a problem connecting to a remote endpoint. Raised only when connecting, such as when getting a stream.
GT.Net.CannotSendMessagesError
Thrown when a set of messages could not be sent for various reasons. This is a composite exception. The exception contains the set of messages that could not be sent; the handler can decide whether to re-send the messages.
GT.Net.NoMatchingTransport
Thrown when there is no transport that meets the message/channel delivery requirements

All GT exceptions and error events have an assessment as to their severity:

Fatal
an error has occurred such that GT cannot continue in its operation.
Error
an error has occurred; the application will likely be able to continue, but GT functionality may be significantly limited.
Warning
an error has occurred such that GT is able to continue, but the application's functionality may be compromised
Information
a problem has occurred but has been dealt with; the error is being reported purely for information

Errors/exceptions reported using the error event mechanism are notified using an ErrorSummary object. The ErrorSummary provides:

There are currently 5 different forms of a SummaryErrorCode which represent the implications of the error:

UserException
there was an exception trapped when running application-provided code (e.g., thrown from an event listener);
RemoteUnavailable
the remote system could not be contacted;
MessagesCannotBeSent
there were message(s) that could not be sent; these messages have been dequeued and are available in the exception;
InvalidIncomingMessage
there was an error unmarshalling a message from an incoming connection;
TransportBacklogged
a transport has too much information awaiting; the data has been queued for later transmission, but may be significantly delayed.

Reporting Feedback, Problems, and Requesting Help

We're interested in hearing about your experiences with GT! Feel free to send feedback, criticisms, and questions to both Brian de Alwis (brian.de.alwis@usask.ca) and Carl Gutwin (gutwin@cs.usask.ca). Please report any problems, etc. through the GT Trac at:

https://papyrus.usask.ca/trac/gt/

The Trac also hosts a list of Frequently Asked Questions.