TCP/IP services are accessed through the sockets classes provided by
the Sockets Client API. The principal class is RSocket
: this
interface encourages a particular type of application structure, which is
somewhat different from Windows or UNIX applications that use the standard
(BSD-style) sockets interface.
Instead of using blocking calls, the RSocket
class
defines the functions for such operations as reading and writing data to be
asynchronous. This allows a program to run in a single thread and remain
responsive to other events. To take the example of a read operation, a typical
sequence is:
An application makes a read request (for example with
RSocket::Read()
), passing a reference to an empty buffer to the
socket server.
The application continues with its processing, typically waiting for and processing other events, such as user commands.
Meanwhile, the socket server monitors the socket for the appropriate amount of data to be received. When this condition is met, it fills the buffer and signals the application.
The application’s event loop processes the event by calling the appropriate handler — which in this case would usually read the data from the buffer.
An encapsulation of the event wait loop and handler despatcher is
provided in the active scheduler. The active scheduler requires that the
functions that make requests to a provider of asynchronous services, together
with a function that handles request completion, are encapsulated in an active
object class. Let’s imagine that we have written such an active object class,
CRead
, derived from CActive
, to wrap the read from
socket operation. It has a request function, CRead::ReadRequest()
,
and implements the active object handler function, RunL()
.
The sequence outlined above is as follows:
As part of the handling of some previous event, the application
calls CRead::ReadRequest()
to issue a read request. This function
makes the call to RSocket::Read()
, passing a reference to an empty
buffer to the socket server.
Control returns to the application’s active scheduler, which waits for further events and calls the appropriate handlers.
Meanwhile, the socket server monitors the socket for the appropriate amount of data to be received. When this condition is met, it fills the buffer and signals the application.
The application’s active scheduler processes this event, and
calls the handler CRead::RunL()
. This function reads the data from
the buffer, and takes other appropriate action.
Applications usually define a number of active object classes in this way to wrap each of their significant asynchronous socket operations: i.e., reading, writing, connection/shutdown, and asynchronous control operations.
You will need to decide at design-time what active object classes should wrap which sockets operations. The limiting factor is that an active object can have only a single request active at any one time. This would mean, for example, that wrapping read and write operations in a single class would mean these requests could not be active in the same object at the same time. If your application requires simultaneous read and write operations, you could:
wrap both operations in the same class, and have two objects
wrap each operation in a different class, and have an instance of each