Sending data between Client and server

4D - Documentation   Français   English   German   4D Plugin API, Command Theme List   4D Plugin API, Command Alphabetical List   Back   Previous   Next

version 2003


Network communication between the server and client instances of 4D plug-ins

Thanks to its Virtual Network layer and the Network Components, 4D Server is able to concurrently communicate with 4D Clients via different network protocols. All 4D Server internal communication services of are based on this architecture.

Two of these communications are made available to 4D plug-ins through the routines services PA_SendDataToServer and PA_ReceiveDataFromServer. These routines allow the 4D plug-in to exchange data between the server and client instances of the Plug-in.

Using these routines frees the developer from handling network issues. Requests are handled by the 4D Server Virtual Network layer and the Network Components. In other words, the 4D plug-in requests become part of the dialog between 4D Server and 4D Client.

The 4D Server READ DATA services

The routine PA_ReceiveDataFromServer allows a 4D plug-in's client instance to request some data from a 4D plug-in's server instance. To do so, the 4D plug-in's client instance passes the following parameters to the ReceiveDataFromServer routine :

- The ID number of 4D plug-in's server instance

- The Requested Data ID number

- The Requested Data Type (4-byte OSType)

- A pointer to the buffer that will receive the data

When such a call is made from a 4D Client, 4D Server calls the server instance of the 4D plug-in whose ID number has been passed, if it exists, and set the selector value of the PluginMain routine of the plug-in's Server instance to kReadOnServer. The 4D plug-in's server instance is also passed the other parameters using a ReadWriteBlock data structure, defined in the header file "PublicTypes.h" as:

typedef struct PA_ReadWriteBlock
{
   unsigned long   fDataType;
   short         fDataID;
   long         fDataSize;
   long         fPackID;
   long         fProcessID;
} PA_ReadWriteBlock;

The 4D server plug-in instance receives a pointer to this structure in the field fParameters of the PluginBlock structure pointed to by the "usual" PluginParameters, in the PluginMain routine.

Upon receipt of this call, the 4D plug-in's server instance, depending on the type and ID of the requested data, decides whether to send data or not.

If data needs to be sent back, the 4D plug-in's server instance MUST:

Allocate a block of memory, copy the requested data into it, then pass a pointer to this block in the package fResult field (of the PA_PluginBlock structure). If you use a MacOS handle, you lock this handle and pass the master pointer of this handle.

Pass the length of this block in the field fDataSize of the ReadWriteBlock data structure.

IMPORTANT NOTE:

The memory block whose address is returned in fResult belongs to the 4D plug-in's server instance. Once the data have been sent over the network, 4D Server will call back the 4D plug-in's server instance with selector set to kServerCleanUp. At this point the 4D plug-in's server instance will be able to dispose of the block.

If NO data has to be sent back the 4D plug-in's server instance MUST:

Return 0L in the fResult.

Pass 0 (zero) in the field fDataSize of the ReadWriteBlock data structure.

Once the 4D plug-in's server instance gives the control back to 4D Server, the network communication service starts sending the data (if any) to the 4D plug-in's client instance.

Once the data has been sent to the client, 4D Server calls back the 4D plug-in's server instance with opCode set to kServerCleanUp, passing a pointer to the ReadWriteBlock data structure in fParameters and the pointer to the data returned previously still in fResult. At this point the 4D plug-in's server instance can dispose of the data if no longer required.

IMPORTANT NOTE: On the client side, the 4D plug-in waits for the network I/O completion. The routine PA_ReceiveDataFromServer returns control to the 4D plug-in when the data (if any) has been received. On the server side the 4D plug-in is called twice, once for providing the data to be sent back, once for optionally disposing of the data after it has been sent.

4D Server WRITE DATA services

The routine PA_SendDataToServer allows an 4D plug-in's client instance to send some data to an 4D plug-in's server instance. To do so, the 4D plug-in's client instance passes the following information to the PA_SendDataToServer routine:

- The ID number of 4D plug-in's server instance

- The Requested Data ID number

- The Requested Data Type (4-byte OSType)

- A pointer to data to be written and its size

When such a call is made from a connected 4D Client, 4D Server calls the server instance of the 4D plug-in whose ID number has been passed, if the server instance exists, and call the plug-in with a selector value set to kWriteOnServer.

The 4D plug-in's server instance is also passed:

a pointer to a ReadWriteBlock data structure in the parameter fParameters of the PA_PluginBlock structure (the one that is pointed to by a PA_PluginParameters in the PluginMain routine).

a pointer to the data that has been received in the fResult field of the PA_PluginBlock.

IMPORTANT NOTE: The pointer passed in fResult belongs to 4D Server. The 4D plug-in's server instance must copy the data if it intends to do something with it later on.

Upon reception of this call the 4D plug-in's server instance, depending on the type and ID of the data, performs the appropriate actions.

Note that:

The 4D plug-in can choose whatever values are needed for the type and the ID number of the data it reads or writes. These are private conventions.

The 4D plug-in's server instance is fully responsive to the temporary or permanent, memory-based or disk-based storage handling of the data exchanged with the client instances of the 4D plug-in(s).

4D Server does not interfere with the data, it simply insures the network transport of the data.

The READ DATA and WRITE DATA services are Client/Server oriented. Client instances of 4D plug-in(s) can send requests to server instances of 4D plug-in(s). The reverse is NOT true. A 4D plug-in's server instance cannot send a request to any 4D plug-in's client instance.

Other considerations while executing the server instance of a 4D plug-in:

The developer must be very careful while debugging code. If the server instance of a 4D plug-in crashes it will interrupt the work of all the users connected to the server.

Avoid the execution of time-consuming operations as these types of operations will slow down all users who are connected to the server.

Limit, as much as is possible, the exchange of large amounts of data through the READ DATA and WRITE DATA services. This will slow down the network activity for all users connected to the server.

Limit, as much as is possible, the implementation of user interface components. The main purpose of the server machine is to serve, not be used as work station.

Do NOT spend development resources and time developing 4D plug-ins running on the server machine whose purpose is to provide a 4D Server service that replicates functionality of 4th Dimension or 4D Client. Future versions of the 4D Client/Server architecture would most likely obsolete such 4D plug-ins.

Sample code using the Send/Data functions

/*
   When the 4D Client plug-in instance wants to read some data,
   It calls PA_GetDataFromServer with the appropriates parameters.
   
   As an example with the pre-defined constants of this sample,
   to get a small icon form the server, the client use:
   
   char *icon;
   buffer = malloc(SIZEOF_SMALL_ICON);
   if(icon)
   {
      PA_GetDataFromServer(kSignature, kSmallIcon, kPICTType, icon);
      // . . . deal with icon . . . 
   }
      
   When the 4D Client plug-in instance wants to send some data to
   the server, it calls PA_SendDataToServer:
   // assuming icon is a regular pointer to the good data
   PA_SendDataToServer(kSignature, kSmallIcon, kPICTType, icon, size);
*/
#include "4DPluginAPI.h"
#include "PublicTypes.h"
//   Constants:
#define kSignature      ((long)16789)
#define kPICTType   'PICT'
   #define kSmallIcon   15000
   #define kBigIcon   15001
   
#define kTEXTType   'TEXT'
   #define kFullPath   15000
   #define kPrefs      15001
   
#define kBLOB      'BLOB'
   #define kBlob1   15000
   #define kBlob2   15001
void PluginMain(long selector, PluginParameters params)
{
   switch(selector)
   {
   // Initialization (both Client and server instances), server stuff
      /* ... kInitPlugin...kServerInitPlugin...*/
      
   // A client send some data with PA_SendDataToServer
      case kWriteOnServer:
         GetFromClient(params);
         break;
   
   // A client requests some data with PA_ReadDataOnServer
      case kReadOnServer:
         SendToClient((ReadWritePtr)params, result);
         break;
   
   // The client has received the data: time to cleanup
      case kServerCleanUp:
         CleanServer(result);
         break;
   /*
      case first_routine:
         . . .
         break;
      
      case second_routine:
         . . .
         break;
   */
   }
}
//   PA_SendDataToServer had been executed on the workstation:
void GetFromClient(PluginParameters params)
{
   ReadWriteBlock *rwb;
   char      *data;
   
   // Get the sent data info, and a pointer to the data itself
   rwb = (ReadWriteBlock *) params->fParameters;
   data = params->fResult;
   
// It may be useful to check that the received data
// is really the one we expect (a magic number could
// be add to all data, and checked here)
   
   //   So let's check which kind of data the client has sent
   switch(rwb->fDataType)
   {
   // We are receiving a picture (icons in this sample)
      case kPICTType:
         switch(rwb->fDataID)
         {
            case kSmallIcon:
               /* Do something for this kind of data *//
               break;
               
            case kBigIcon:
               /* Do something for this kind of data *//
               break;
         }
         break;
         
   // We are receiving text (path, prefs)
      case kTEXTType:
         switch(rwb->fDataID)
         {
            case kFullPath:
               /* Do something for this kind of data *//
               break;
               
            case kPrefs:
               /* Do something for this kind of data *//
               break;
               
         }
         break;
   
   // We are receiving special data
      case kBLOB:
         switch(rwb->fDataID)
         {
            case kBlob1:
               /* Do something for this kind of data *//
               break;
               
            case kBlob2:
               /* Do something for this kind of data *//
               break;
               
         }
         break;
   }
}
//   PA_ReceiveDataFromServer had been executed on the workstation:
void SendToClient(PluginParameters params)
{
   OSErr   err;
   SLONG   size;
   char   *buffer;
   
   ReadWriteBlock      *rwb;
   
   // Get the requested data info
   rwb = (ReadWriteBlock *) params->fParameters;
   
   //   Let's check what kind of data is requested by the client.
   switch(rwb->fDataType)
   {
   // The client wants an icon
      case kPICTType:
         switch(rwb->fDataID)
         {
            case kSmallIcon:
         //   Create the buffer we have to return:
               size = SIZE_OF_SMALL_ICON;
               buffer = malloc(size);
               if(buffer)
               {
               /* . . . Fills buffer with the icon data . . . */
                  *params->fResult = (long)buffer;
                  rwb->fDataSize = size;
               }
               break;
               
            case kBigIcon:
         //   Create the buffer we have to return:
               size = SIZE_OF_BIG_ICON;
               buffer = malloc(size);
               if(buffer)
               {
               /* . . . Fills buffer with the icon data . . . */
                  *params->fResult = (long)buffer;
                  rwb->fDataSize = size;
               }
               break;
         }
         
   // The client wants a text
         case kTEXTType:
            switch(rwb->fDataID)
            {
         /*
            This is like kPICTType:
               - switch the type of data (kFullPath, kPrefs)
               - For each kind of data:
                  Allocate a buffer
                  fill it with the appropriate data
                  set rwb->fDataSize to its size
                  set *params->fResult to this buffer
         */
            }
            break;
   
   // The client wants a special data
         case kTEXTType:
            switch(rwb->fDataID)
            {
         /*
            This is like kPICTType:
               - switch the type of data (kBlob1, kBlob2)
               - For each kind of data:
                  Allocate a buffer
                  fill it with the appropriate data
                  set rwb->fDataSize to its size
                  set *params->fResult to this buffer
         */
            }
            break;
         
      break;
   } // switch(rwb->fDataType)
}
// The server has sent data to the client.
// The client has received it, and 4D ask us to cleanup
// memory if necessary (we could keep the data if we need it)
void CleanServer(PluginParameters params)ResultPtr result)
{
   char    *data;
   data = *params->fResult;
   
   if(data)
   {
      free(data);
      *params->fResult = 0L;
   }
}

4D - Documentation   Français   English   German   4D Plugin API, Command Theme List   4D Plugin API, Command Alphabetical List   Back   Previous   Next