This program is the client part of the sample client/server application
that began in Sample APPC/MVS Server.
Sample Error Routine and Header File shows the source code for
the error routine (SRVERROR) used by this application, and the C language
header file used to define error codes for the SRVERROR routine.
/**********************************************************************/
/* This program is a sample client for a client/server pair */
/* written in C/370 to demonstrate the use of APPC/MVS allocate */
/* queue services. This client program uses the CPI-C */
/* interface and uses no MVS-specific services. */
/* */
/* */
/* COPYRIGHT -- */
/* */
/* (C) Copyright IBM Corp. 1992 */
/* All rights reserved. */
/* U.S, Government Users Restricted Rights -- Use, */
/* duplication, or disclosure restricted by GSA ADP Schedule */
/* Contract with IBM Corp. Program Property of IBM. */
/* */
/* This program is provided to you for tutorial purposes only. */
/* You may not use the program for commercial purposes. */
/* This program is a sample working solution intended */
/* to show the use of APPC/MVS allocate queue services. */
/* Independent of its particular use, this program is */
/* supplied as an example and provided "as is" without */
/* warranty of any kind, either express or implied, including, */
/* but not limited to, the implied warranties of */
/* merchantability and fitness for a particular purpose. */
/* The entire risk about the quality and performance of the */
/* program is with you. Should the program prove defective, */
/* you assume the entire cost of all necessary servicing, */
/* repair, or correction. */
/* */
/* In no event will IBM be liable to you for any damages or */
/* any lost profits, lost savings or other incidental or */
/* consequential damages arising out of the use of or */
/* inability to use the program even if IBM had been advised */
/* of the possibility of such damages, or for any claim by any */
/* other party. */
/* */
/* */
/* MODULE NAME -- */
/* */
/* DRV1.C */
/* */
/* */
/* ENTRY POINTS -- */
/* */
/* Normal C entry executed on the client platform. */
/* */
/* */
/* STATUS -- */
/* */
/* Version 1, Release 0 */
/* */
/* */
/* FUNCTION -- */
/* */
/* This program is provided as an example of APPC/MVS */
/* allocate queue services. This program is the client half */
/* of a client/server pair. It invokes the server and waits */
/* for the server to respond by sending data. */
/* */
/* */
/* INPUT -- */
/* */
/* None */
/* */
/* OUTPUT -- */
/* */
/* None. */
/* */
/* */
/* RETURN INFORMATION -- */
/* */
/* None. */
/* */
/* */
/* CHANGE HISTORY -- */
/* */
/* 08/31/91 - Module created. */
/* */
/**********************************************************************/
/**********************************************************************/
/* */
/* Include the header files which define the services used by */
/* this program. STDIO and STRING are standard C libraries. */
/* ATBCMC is the interface definition file (IDF) for the CPI-C */
/* services. ERRCDE is the header file for the srverror function */
/* that handles error conditions detected by this program. */
/* */
/* The '#pragma runopts(execops)' is a C/370 option which permits */
/* the caller to specify runtime options to C/370 before */
/* specifying parameters to this program. This was done to permit */
/* the caller to specify /NOSPIE and /NOSTAE to prevent C/370 */
/* from suppressing a user abend which might be generated by the */
/* srverror function. */
/* */
/**********************************************************************/
#pragma runopts(execops)
#include <STDIO.H>
#include <STRING.H>
#include <ATBCMC.H>
#include <ERRCDE.H>
/**********************************************************************/
/* */
/* MAINLINE CODE */
/* */
/**********************************************************************/
main()
{
/**********************************************************************/
/* */
/* DECLARE VARIABLES */
/* */
/* conv_id - conversation identifier returned by APPC on the CMINIT */
/* call and used on subsequent calls */
/* */
/* sym_dest - symbolic destination name identifying the server */
/* (TP name, LU name, and logon mode). Note that this */
/* variable is one character longer than the symbolic */
/* destination name parameter. Since a value is placed */
/* in this parameter using the strcpy() function we must */
/* provide an extra character for the null since it */
/* must not be a part of the passed value. */
/* */
/* return_code - used to hold return codes from APPC services */
/* */
/* buffer - The buffer which is used to receive data from the */
/* partner program (server). The buffer is 19 characters */
/* long because we know that is how much data the server */
/* will be sending. */
/* */
/* requested_length - The length of the buffer we will provide to */
/* the receive (CMRCV) service. */
/* */
/* data_received - A returned parameter from the CMRCV service which */
/* will indicate whether any data was received from */
/* the server. */
/* */
/* received_length - A returned parameter from the CMRCV server which*/
/* will indicate the amount of data placed into */
/* our buffer by APPC. */
/* */
/* status_received - A returned parameter from the CMRCV service */
/* which will indicate whether any status was */
/* received from the server. */
/* */
/* rts_received - A returned parameter from the CMRCV service which */
/* indicates whether the partner has requested send */
/* control. Should always be set to */
/* rts_not_received in this application since this */
/* program always immediately grants the server */
/* send control. */
/* */
/* srverror_return_code - The return code from the srverror function.*/
/* */
/**********************************************************************/
char conv_id[8];
char sym_dest[9];
long int return_code;
char buffer[19];
long int requested_length;
long int data_received;
long int received_length;
long int status_received;
long int rts_received;
int srverror_return_code;
/**********************************************************************/
/* */
/* Set the sym_dest variable to the symbolic destination name. */
/* There must be an entry defined in the side information table */
/* for this value. It must contain an LU name and TP name which */
/* correspond to the values for which the server has registered. */
/* See APPC/MVS Planning and Management for information about adding */
/* Side Information. */
/* */
/* The srverror function return code is initialized to zero.
/* */
/**********************************************************************/
strcpy(sym_dest,"SRVORDER");
srverror_return_code = 0;
/**********************************************************************/
/* */
/* INITIALIZE THE CONVERSATION */
/* */
/* Call the Initialize_Conversation service (CMINIT), providing */
/* the symbolic destination name defined just above. If all goes */
/* well, a conversation identifier will be returned in the conv_id */
/* variable. */
/* */
/* If all is not well (i.e. the return code is not CM_OK) then */
/* invoke the srverror function providing a description of the */
/* problem encountered (CMINIT return code error), the expected */
/* return code value, and the actual return code received. */
/* The srverror function will then act appropriately and */
/* return a return code indicating whether to continue processing. */
/* */
/* Of course, in this case any non-zero return code should result */
/* in termination of processing; the srverror function, however, */
/* can update an error log, issue an operator message, or take other */
/* appropriate action. */
/* */
/**********************************************************************/
CMINIT(conv_id,
sym_dest,
&return_code);
if (return_code != CM_OK)
{
error_description.problem = CMINIT_RET_CODE_ERROR;
error_description.error_reason.rc_problem.expected_return_code =
CM_OK;
error_description.error_reason.rc_problem.actual_return_code =
return_code;
srverror_return_code = srverror(error_description);
}
/**********************************************************************/
/* */
/* ALLOCATE THE CONVERSATION */
/* */
/* If all is well, allocate the conversation by calling the CMALLC */
/* service. The only input parameter is the conversation identifier */
/* returned by CMINIT. */
/* */
/* If the allocate function fails (non-zero return code), we again */
/* check with the srverror function to find out if we should */
/* continue processing. */
/* */
/**********************************************************************/
if (srverror_return_code==0)
{
CMALLC(conv_id,
&return_code);
if (return_code != CM_OK)
{
error_description.problem = CMALLC_RET_CODE_ERROR;
error_description.error_reason.rc_problem.expected_return_code =
CM_OK;
error_description.error_reason.rc_problem.actual_return_code =
return_code;
srverror_return_code = srverror(error_description);
}
}
/**********************************************************************/
/* */
/* RECEIVE DATA FROM THE SERVER */
/* */
/* Next two tasks are accomplished by calling one function. When */
/* the CMRCV service is called from send state, notification is */
/* first sent to the server that it has been granted send control */
/* and then this program waits for the server to send data. */
/* Note that we set the requested_length parameter to the size of */
/* the receive buffer. */
/* */
/* Next the returned parameters will be examined and the srverror */
/* function invoked if any unexpected results occur. */
/* */
/**********************************************************************/
if (srverror_return_code==0)
{
requested_length = sizeof(buffer);
cmrcv (conv_id,
buffer,
&requested_length,
&data_received,
&received_length,
&status_received,
&rts_received,
&return_code);
/**********************************************************************/
/* */
/* The first returned parameter to examine is naturally the return */
/* code. Two values may be expected on this call. Since we know */
/* the partner will deallocate the conversation after sending the */
/* data, we expect to get a return code of CM_DEALLOCATED_NORMAL. */
/* It is possible, however, that the return code may not have arrived*/
/* yet, so a return code of CM_OK might be returned. If neither */
/* value is found, the srverror function is invoked. */
/* */
/**********************************************************************/
if (return_code != CM_OK)
if (return_code != CM_DEALLOCATED_NORMAL)
{
error_description.problem = CMRCV_RET_CODE_ERROR;
error_description.error_reason.rc_problem.
expected_return_code = CM_OK;
error_description.error_reason.rc_problem.
actual_return_code = return_code;
srverror_return_code = srverror(error_description);
}
/**********************************************************************/
/* */
/* If the return code is OK or DEALLOCATED_NORMAL, then we can */
/* examine the data received field to determine if any data was */
/* received. If data was received, then we can examine the */
/* received_length field to determine how much data was received. */
/* If we received the expected length, then we can proceed to */
/* examine the data itself to verify it is as expected. */
/* */
/* Note that in a real application the actual value of the expected */
/* data would probably not be known, but this check can be easily */
/* replaced with a check verifying that the data is in some expected */
/* format (for example, if the expected data were inventory record */
/* updates you might expect the data to consist of item identifiers */
/* and quantities in four byte integer pairs). */
/* */
/* If any returned values are not as expected, the srverror function */
/* is invoked. */
/* */
/**********************************************************************/
if ((return_code==CM_OK)|(return_code==CM_DEALLOCATED_NORMAL))
{
if (data_received!=CM_COMPLETE_DATA_RECEIVED)
{
error_description.problem = CMRCV_DATA_RCV_ERROR;
error_description.error_reason.data_rcv_problem.
expected_data_rcv = CM_COMPLETE_DATA_RECEIVED;
error_description.error_reason.data_rcv_problem.
actual_data_rcv = data_received;
srverror_return_code = srverror(error_description);
}
if (srverror_return_code == 0)
{
if (received_length != requested_length)
{
error_description.problem = CMRCV_RCVD_LEN_ERROR;
error_description.error_reason.length_problem.
expected_length = requested_length;
error_description.error_reason.length_problem.
actual_length = received_length;
srverror_return_code = srverror(error_description);
}
if (srverror_return_code == 0)
{
if (strcmp(buffer,"123456789012345678"))
{
error_description.problem = CMRCV_BUFFER_ERROR;
error_description.error_reason.data_problem.
expected_data = "123456789012345678";
error_description.error_reason.data_problem.
actual_data = buffer;
srverror_return_code = srverror(error_description);
}
}
}
/**********************************************************************/
/* */
/* At this point we have verified that all the information we */
/* expected to receive has arrived. We have not examined the status */
/* received field since we expected to receive no status. Just to */
/* be complete, we will verify that we did in fact receive no status */
/* and invoke the srverror function if status did turn up. */
/* Note that this check occurs inside a conditional which ensures */
/* that we only examine the status_received field when the return */
/* code is CM_OK since the status field is not set for the */
/* CM_DEALLOCATED_NORMAL return code (or other non-zero return codes)*/
/* */
/**********************************************************************/
if (srverror_return_code == 0)
{
if (return_code == CM_OK)
{
if (status_received != CM_NO_STATUS_RECEIVED)
{
error_description.problem = CMRCV_STATUS_ERROR;
error_description.error_reason.status_problem.
expected_status = CM_NO_STATUS_RECEIVED;
error_description.error_reason.status_problem.
actual_status = status_received;
srverror_return_code = srverror(error_description);
}
}
}
}
}
/**********************************************************************/
/* */
/* At this point, the client has received the data from the server */
/* and could perform any processing required such as updating a */
/* local database with new information. */
/* */
/**********************************************************************/
/**********************************************************************/
/* */
/* As mentioned above, it is possible the notification of the end */
/* of the conversation might not have arrived on the first receive */
/* as a DEALLOCATED_NORMAL return code. In this case, we need to */
/* issue another CMRCV to get this return code. Note that we set */
/* the requested_length to zero for this receive since we expect no */
/* data to arrive. After the receive completes, the return code */
/* is inspected and the srverror function is invoked if an */
/* unexpected value is found. We also examine the data_received */
/* field to verify it is set to CM_NO_DATA_RECEIVED. */
/* */
/**********************************************************************/
if (srverror_return_code == 0)
{
if (return_code == CM_OK)
{
requested_length = 0;
cmrcv (conv_id,
buffer,
&requested_length,
&data_received,
&received_length,
&status_received,
&rts_received,
&return_code);
if (return_code != CM_DEALLOCATED_NORMAL)
{
error_description.problem = CMRCV_RET_CODE_ERROR;
error_description.error_reason.rc_problem.
expected_return_code = CM_DEALLOCATED_NORMAL;
error_description.error_reason.rc_problem.
actual_return_code = return_code;
srverror_return_code = srverror(error_description);
}
if (srverror_return_code == 0)
{
if (data_received != CM_NO_DATA_RECEIVED)
{
error_description.problem = CMRCV_DATA_RCV_ERROR;
error_description.error_reason.data_rcv_problem.
expected_data_rcve = CM_NO_DATA_RECEIVED;
error_description.error_reason.data_rcv_problem.
actual_data_rcv = data_received;
srverror_return_code = srverror(error_description);
}
}
}
}
/**********************************************************************/
/* */
/* The function of the client is complete. */
/* */
/**********************************************************************/
return srverror_return_code;
}