In the following example, a client sends the contents of a file to a server as a set of chunks allocated from the same static buffer. The chunks are processed (in this case simply printed) as they arrive.
The declaration in the interface definition is as follows:
typedef pipe char test_pipe_t;
void pipe_test1(
[in] handle_t handle,
[in] test_pipe_t test_pipe,
[out] error_status_t *status
);
Note that the pipe is declared as a typedef, resulting in an IDL-generated C typedef for test_pipe_t, which is a structure containing pointers to the pipe support routines and a pipe state field. The server manager and client code then implement the pipe in a complementary fashion.
For an [in] pipe, the server manager code consists of a cycle of calls to the test_pipe.pull routine (a server stub routine) which terminates when a zero-length chunk is received:
void
pipe_test1(
handle_t binding_h,
test_pipe_t test_pipe,
error_status_t *status
)
{
char buffer[SBUFFSIZE];
int count;
char *cptr;
do
{
(*(test_pipe.pull))(test_pipe.state, buffer, \
SBUFFSIZE, &count);
for (cptr = buffer; cptr < buffer + count; cptr++)
putchar(*cptr);
} while (count > 0);
}
Using the buffer supplied by the manager, the test_pipe.pull routine unmarshals an amount of data that is nonzero, but not more than the buffer can hold. There is no guarantee that the buffer will be filled. The actual amount of data in the buffer is indicated by the count parameter returned in the test_pipe.pull routine. This count equals the number of test_pipe_t data elements in the buffer.
The test_pipe.pull routine signals the end of data in the pipe by returning a chunk whose count is 0 (zero). Any attempt to pull data from the pipe after the zero-length chunk has been encountered will cause an exception to be raised. The in pipes must be processed in the order in which they occur in the operation signature. Attempting to pull data from an in pipe before end-of-data on any preceding in pipe has been encountered will result in an exception being raised. If the manager code attempts to write to an out pipe or return control to the server stub before end-of-data has been encountered on the last in pipe, an exception will be raised. (Note that there is no guarantee that chunks seen by the manager will match the chunks supplied by the client's pull routine.)
The client application code must supply pull and alloc routines and a pipe state. These routines must work together to produce a sequence of pointers to chunks, of which only the last is empty. In the following example, the client code provides a test_pipe.pull routine that reads chunks of the input file into a buffer and returns a count of the chunk size, returning a zero count when the end of the file is reached. The pipe state block is used here simply as a convenient way to make the file state available to the pull routine. Applications need not make any use of the pipe state.
/* Client declares types and routines */
typedef struct client_pipe_state_t {
idl_char *filename;
idl_boolean file_open;
int file_handle;
} client_pipe_state_t;
client_pipe_state_t client_in_pipe_state = {false, 0};
void client_pull(state,buf,esize,ecount)
client_pipe_state_t * state;
byte *buf;
unsigned int esize;
unsigned int *ecount;
{
if ( ! state->file_open )
{
state->file_handle = open(state->filename,O_RDONLY);
if (state->file_handle == -1)
{
printf("Client couldn't open %s\n", state->filename);
exit(0);
}
state->file_open = true;
}
*ecount = read( state->file_handle, buf, esize );
if (*ecount == 0)
{
close(state->file_handle);
state->file_open = false;
}
}
Finally, the client must do the following:
1. Allocate the test_pipe_t structure.
2. Initialize the test_pipe_t.pull, test_pipe_t.alloc, and test_pipe_t.state fields.
3. Include code where appropriate for checking the pipe_t.state field.
4. Pass the structure as the pipe parameter. The structure can be passed either by value or by reference, as indicated by the signature of the operation that contains the pipe parameter:
/* Client initializes pipe */
test_pipe_t test_pipe;
test_pipe.pull = client_pull;
test_pipe.alloc = client_alloc;
test_pipe.state = (rpc_ss_pipe_state_t)&client_in_pipe_state;
/* Client makes call */
pipe_test1(binding_h, test_pipe, &status);
To transmit a large amount of data that is already in the proper form in memory (that is, the data is already an array of test_pipe_t), the client application code can have the alloc routine allocate a buffer that already has the information in it. In this case, the pull routine becomes a null routine.