Common

In order for the client and server to communicate, they must agree on a common message protocol. Creating a message protocol requires the following:

  • A set of C data structures for exchanging information
  • A corresponding set of type specifications which describe the layout and relationships of these structures
  • A protocol specification which binds a set of message tags and payload types together into a description of the protocol

Because these elements are the same for both the client and server, they are factored out into their own library in the interest of efficiency.

The include file protocol.h contains the basic C type definitions and message tag enumeration for the Fserv message protocol. The type and protocol specifications are contained in protocol.c.

Message Structures

First, we define the types that will be used to transmit data between client and server:

/* Begin message structures */
/* Opaque type -- actual definition in protocol-server.h */
typedef struct FileHandle FileHandle;
typedef enum OpenMode
{
OPEN_MODE_READ = 1,
OPEN_MODE_WRITE = 2,
OPEN_MODE_APPEND = 4
} OpenMode;
/* Parameters sent in an open request */
typedef struct OpenRequest
{
char* path;
OpenMode mode;
} OpenRequest;
/* Parameters sent in a write request */
typedef struct WriteRequest
{
LWMsgHandle* handle;
unsigned long size;
char* data;
} WriteRequest;
/* Parameters sent in a read request */
typedef struct ReadRequest
{
LWMsgHandle* handle;
unsigned long size;
} ReadRequest;
/* Generic status code reply for faild open, failed read, write, close */
typedef struct StatusReply
{
int err;
} StatusReply;
/* Reply to a successful read request */
typedef struct ReadReply
{
unsigned long size;
char* data;
} ReadReply;
/* Notably missing structures:
*
* OpenReply - the reply type is just a LWMsgHandle if it succeeds
* CloseRequest - the request type is just a LWMsgHandle
*/
/* End message structures */

Note the presence of the opaque FileHandle structure. This type will be transmitted to the client as a handle. The actual definition of this structure is placed in protocol-server.h which the client does not include. This prevents client code from erroneously attempting to dereference opaque handle types.

Also note that there are no OpenReply or CloseRequest structures. This is because both can be represented as FileHandles without being wrapped in an outer structure – LWMsg permits any pointer or pointer-like type to be the payload of a message.

Message Tag Enumeration

We then enumerates all possible message tags. These tags comprise the set of messages that may be sent between client and server. Each element of the enumeration has a comment specifying the C structure which is the payload for that type of message.

/* Begin message enumeration */
typedef enum MessageType
{
FSERV_OPEN_REQ, /* OpenRequest */
FSERV_OPEN_RES, /* LWMsgHandle */
FSERV_READ_REQ, /* ReadRequest */
FSERV_READ_RES, /* ReadReply */
FSERV_WRITE_REQ, /* WriteRequest */
FSERV_CLOSE_REQ, /* LWMsgHandle */
FSERV_VOID_RES, /* void */
FSERV_ERROR_RES /* StatusReply */
} MessageType;
/* End message enumeration */

There is a request tag and response tag for each call we want to support. There is also a generic void tag which is used for call responses that contain no data, and a generic error tag for call responses that indicate an error.

LWMsg does not impose limitations on when certain message types can be sent or by whom. Although LWMsg guarantees that all message payloads are well-formed, it is up to the application to ensure that they are used appropriately.

Type Specification

Each C structure that we want to use in the message protocol must have a type specification describing it:

/* Begin type specifications */
LWMsgTypeSpec openmode_spec[] =
{
/* Marshal enum OpenMode as an 8-bit unsigned integer */
/* Bitfield definitions */
LWMSG_ENUM_MASK(OPEN_MODE_READ),
LWMSG_ENUM_MASK(OPEN_MODE_WRITE),
LWMSG_ENUM_MASK(OPEN_MODE_APPEND),
/* End enum */
/* End type */
};
/* FileHandle (opaque) */
LWMsgTypeSpec filehandle_spec[] =
{
/* Identify type name of handle */
LWMSG_HANDLE(FileHandle),
/* End specification */
};
/* OpenRequest */
LWMsgTypeSpec openrequest_spec[] =
{
/* Begin structure */
LWMSG_STRUCT_BEGIN(OpenRequest),
/* path - marshal as pointer to string */
LWMSG_MEMBER_PSTR(OpenRequest, path),
/* mode - marshal as OpenMode enum */
LWMSG_MEMBER_TYPESPEC(OpenRequest, mode, openmode_spec),
/* End structure */
};
/* WriteRequest */
LWMsgTypeSpec writerequest_spec[] =
{
/* Begin structure */
LWMSG_STRUCT_BEGIN(WriteRequest),
/* handle - marshal as FileHandle (references existing spec) */
LWMSG_MEMBER_TYPESPEC(WriteRequest, handle, filehandle_spec),
/* size - marshal as 64-bit unsigned integer */
LWMSG_MEMBER_UINT64(WriteRequest, size),
/* data - marshal as pointer */
LWMSG_MEMBER_POINTER_BEGIN(WriteRequest, data),
/* marshall pointees as 8-bit unsigned integers (in-memory type is char) */
LWMSG_UINT8(char),
/* End pointer */
/* Length of data is equal to value of size member */
LWMSG_ATTR_LENGTH_MEMBER(WriteRequest, size),
/* Data should be printed as hex + ASCII dump */
LWMSG_ATTR_ENCODING("hex+ascii"),
/* End structure */
};
/* ReadRequest */
LWMsgTypeSpec readrequest_spec[] =
{
/* Begin structure */
LWMSG_STRUCT_BEGIN(ReadRequest),
/* handle - marshal as FileHandle (references existing spec) */
LWMSG_MEMBER_TYPESPEC(ReadRequest, handle, filehandle_spec),
/* size - marshal as 64-bit unsigned integer */
LWMSG_MEMBER_UINT64(ReadRequest, size),
/* End structure */
};
/* StatusReply */
LWMsgTypeSpec statusreply_spec[] =
{
/* Begin structure */
LWMSG_STRUCT_BEGIN(StatusReply),
/* err - marshal as 32-bit unsigned integer */
LWMSG_MEMBER_UINT32(StatusReply, err),
/* End structure */
};
/* ReadReply */
LWMsgTypeSpec readreply_spec[] =
{
/* Begin structure */
LWMSG_STRUCT_BEGIN(ReadReply),
/* size - marshal as 64-bit unsigned integer */
LWMSG_MEMBER_UINT64(ReadReply, size),
/* data - marshal as pointer */
LWMSG_MEMBER_POINTER_BEGIN(ReadReply, data),
/* marshall pointees as 8-bit unsigned integers (in-memory type is char) */
LWMSG_UINT8(char),
/* End pointer */
/* Length of data is equal to value of size member */
LWMSG_ATTR_LENGTH_MEMBER(ReadReply, size),
/* Data should be printed as hex + ASCII dump */
LWMSG_ATTR_ENCODING("hex+ascii"),
/* End structure */
};
/* End type specifications */

Protocol Specification

Finally, we need a protocol specification which binds together our enumerated set of messages tags and indicates the payload type of each message.

/* Begin protocol specification */
LWMsgProtocolSpec protocol_spec[] =
{
LWMSG_MESSAGE(FSERV_OPEN_REQ, openrequest_spec),
LWMSG_MESSAGE(FSERV_OPEN_RES, filehandle_spec),
LWMSG_MESSAGE(FSERV_READ_REQ, readrequest_spec),
LWMSG_MESSAGE(FSERV_READ_RES, readreply_spec),
LWMSG_MESSAGE(FSERV_WRITE_REQ, writerequest_spec),
LWMSG_MESSAGE(FSERV_CLOSE_REQ, filehandle_spec),
LWMSG_MESSAGE(FSERV_VOID_RES, NULL),
LWMSG_MESSAGE(FSERV_ERROR_RES, statusreply_spec),
};
/* End protocol specification */