PreviousIntroduction to Communications Programming Common Communications InterfaceNext"

Chapter 2: Communications Programming with CCI

The Common Communications Interface (CCI) is an Application Programming Interface (API) which enables you to build communications capabilities into your applications. CCI is designed to enable a programmer to produce applications which initiate, control, and terminate a conversation with another application on either the same machine, or on another machine on a connected network. The network and its interface are hidden from the programmer and the end user.

2.1 Overview

CCI enables programs to access communications media in a consistent manner, irrespective of the communications protocol or operating environment. Programs written using the CCI API can communicate using any of the supported communications protocols.

CCI comprises a suite of modules, each module supporting a particular communications protocol. For example, the CCITCP module supports the TCP/IP communications protocol.

Using CCI, you can connect applications executing in one user session with other applications executing in another user session on the same machine or on another machine/workstation and transmit data using simple send, receive, suspend, resume, and terminate function calls.

CCI is used by Fileshare Version 2, Fileshare for Host Systems, MTS, Probe, and the cooperative animation facilities of this COBOL system. CCI is also used by the Application to Application Interface (AAI) product to provide communications facilities for Remote Procedure Calls (RPC).

CCI modules are provided for the following communications protocols and environments:

Protocol Module DOS NLM Windows Windows NT OS/2 16-bit OS/2 32-bit UNIX
LU6.2 CCIAPPC x   x   x x,
DDE CCIDDE     x x   ,
Novell IPX CCIIPX x x x   x x,
LU2.0 CCILU2 x   x   x x,
Named Pipes CCINAMP         x x,
UNIX Named Pipes CCINAMPU             x
NETBIOS CCINETB x   x   x x,
TCP/IP CCITCP x   x x x x x


Note: The CCI modules are not supported in virtual environments, such as Windows running under OS/2.


On DOS, Windows and OS/2, the CCI modules are provided as .exe, .dlw, or .dll files so that you can call CCI dynamically from your program. They are also provided in .obj and/or .lib files so that you can statically link the CCI modules with your .obj modules. The .lib files for CCI are named as follows:

DOS:
filenamed.lib

Windows:
filenamew.lib

OS/2:
filename.lib

2.2 CCI Functions

CCI is implemented as a set of API functions which you can call from your program. A summary of the available CCI functions, in alphabetical order, is given below. See the chapter Common Communications Interface for full syntax details.

CCI-Closeclient Requests the disconnection of a client from a server.
CCI-Closeserver Requests the termination of a server.
CCI-Connect Establishes a connection between a server and a client.
CCI-Geterror Returns an error message for the last reported error.
CCI-Hangup Requests the disconnection of a server from a client.
CCI-Initclient Establishes a connection between a client and a server.
CCI-Initserver Initializes a server.
CCI-Query Determines whether an aynchronous request has completed.
CCI-Receive Receives a message.
CCI-Receivall Reads a message from any client targeted at a server.
CCI-Resumeclient Prepares a connection between a client and a server.
CCI-Resumeserver Prepares a connection between a server and a client.
CCI-Send Sends a message.
CCI-Suspendclient Suspends a connection between a client and a server.
CCI-Suspendserver Suspends a connection between a server and a client.
CCI-Timeout Overrides the default timeout.
CCI-Trace Traces information to a log file.
CCI-Transact Executes a synchronous CCI-Send followed immediately by a synchronous CCI-Receive.
CCI-Wait Waits until an asynchronous request has completed.

2.3 The CCI Call Interface

Before your program can use CCI, an initialization call must be made. This call enables the CCI routines to be called via a PROCEDURE-POINTER table which should be located in the Linkage Section of your program. A set of copyfiles are supplied for this purpose:

CCI can be dynamically or statically linked on DOS, Windows, OS/2 and UNIX.

The CCI API is called using the standard COBOL call convention (CALL-CONVENTION 0). The CCI API can be called from C and Assembler programs as well as COBOL.

The CCI API can also be called using the Pascal call convention (CALL-CONVENTION 3) on those environments that support this type of calling convention (DOS, Windows and OS/2). If you use this call convention from COBOL, you will need to set up the Pascal call convention using the CALL-CONVENTION 3 statement in the Special-Names paragraph and use this in each call statement.

Versions of CCI prior to Version 2 (that is, those available with Version 3.0 and earlier of this COBOL system) used only the Pascal call convention and passed some parameters BY VALUE. This interface is documented in the chapter Calling CCI Functions By Value. However, the current COBOL interface is simpler, so you may find it useful to change the calls in any existing code. You can determine the CCI version in use on your system by inspecting the CCIVERSN parameter returned by the CCI initialization call.

The default call convention (CALL-CONVENTION 0) syntax is:

call "module-name" using param-block

where the parameters are:

module-name One of the CCI modules, such as CCINETB, CCIIPX, CCIAPPC or CCITCP.
param-block On return, this block contains a 20-byte signature identifying the module, and a pointer to a table of procedure pointers.

You should test the 20-byte signature to verify that the module called was the correct one.

The table of procedure pointers is used to invoke CCI functions.

The format of param-block is as follows:

 01 param-block.
     03 signature.
         05 CCISIGN     PIC X(6).
         05 CCITYPE     PIC X(8).
         05 CCIVERSN    PIC X(6).
     03 table-pointer   USAGE POINTER.

where on return:

CCISIGN contains "MFCCI-"
CCITYPE contains the name of the required communications protocol, for example, LU2 or TCP/IP
CCIVERSN contains the CCI interface level
table-pointer contains the address of a call table which you should assign to a table of procedure pointers defined in your Linkage Section.

The file ccitab.cpy contains the call table, shown below:

    01 proctab.
        03 CCI-Initserver    procedure-pointer.
        03 CCI-Closeserver   procedure-pointer.
        03 CCI-Initclient    procedure-pointer.
        03 CCI-Closeclient   procedure-pointer.
        03 CCI-Hangup        procedure-pointer.
        03 CCI-Send          procedure-pointer.
        03 CCI-Receive       procedure-pointer.
        03 CCI-Receiveall    procedure-pointer.
        03 CCI-Transact      procedure-pointer.
        03 CCI-Connect       procedure-pointer.
        03 CCI-Wait          procedure-pointer.
        03 CCI-Query         procedure-pointer.
        03 CCI-Resumeclient  procedure-pointer.
        03 CCI-Suspendclient procedure-pointer.
        03 CCI-Resumeserver  procedure-pointer.
        03 CCI-Suspendserver procedure-pointer.
        03 CCI-Geterror      procedure-pointer.
        03 CCI-Trace         procedure-pointer.
        03 CCI-Timeout       procedure-pointer.

Each function sets the special register RETURN-CODE to zero for success, and non-zero for failure. See the section CCI Error Codes in the chapter Common Communications Interface for a list of possible return codes.

2.3.1 Statically Linked Function Names

When CCI is statically linked, the definition of the procedure pointer table in the Linkage Section is not required, but can still be used.

If you do not use the procedure pointer table, the CCI functions can be called by name (for entry point names within the supplied .lib modules) using the same names as those defined in the procedure pointer table above, but using the format "_CCI_protocol_Initclient". For example, to call a statically linked CCI-Initclient from COBOL on machines requiring an underscore (_) prefix before a high-level language reference (for example, any PC running DOS, Windows 3.x or OS/2), you would code the following:

call api "_ CCI_IPX_Initclient" using srvrname
                               machinename
                               sessid
                               async
                               cciend.

On machines not requiring an underscore prefix (for example, some UNIX machines), you omit the leading "_" from CCI_IPX_Initclient.

2.3.2 Server Handles and Session Identifiers

CCI programs use four-byte numeric values (a serverhandle) to distinguish between different servers and four-byte numeric values (a sessionid) to differentiate between different connections within a single client or server.

As CCI uses four-byte numeric handles to denote differing services and connections to those services (sessions), a serverhandle is used in all calls which deal with services as whole elements. These calls are:

A session identifier is used to refer to each unique connection between a client and server. The value of the sessionid can differ between server and client program elements, although within the relevant application segment (server or client) the sessionid always uniquely identifies a specific connection.

A specific sessionid is returned only from CCI-Initclient, CCI-Connect or CCI-Receiveall and is used for every operation that is specific to that session connection from that point onward.

A server can be regarded as an element within a single program that supplies a service which is located on, or visible to, a network. Clients then contact the service, establishing a session with the server.

2.3.3 Synchronous and Asynchronous Calls

Many CCI functions have a four-byte parameter: async. This can be used to implement asynchronous function calls.

If async contains 0, the CCI function executes synchronously; that is, it does not return control to your program until either the operation is complete, an error occurs, or a timeout occurs.

If async contains -1, the CCI function returns control to your program immediately although the function call itself does not complete. If an error occurs, it is reported at this point.

CCI uses the four-byte storage location referred to as async to store a handle containing the return code of the call. This handle can be interrogated by the CCI-Query or CCI-Wait functions. You should not check the value of any parameter passed to a CCI function which is executing asynchronously until either a CCI-Query or CCI-Wait function has completed. Care should be taken when implementing asynchronous function calls. Full support for the asynchronous calls is provided on multi-tasking platforms such as Windows, OS/2 and UNIX. However, DOS systems can suffer performance degradation under certain circumstances because of the way in which some protocol providers implement DOS application interfaces; that is, it is assumed that under DOS, any calling process has complete control of the machine on which it is executing, and a call to the underlying communications interface can have the effect of hanging the machine until the call completes or times-out. This in itself is not a fault, but something that you should be aware of.

When designing applications which make use of asynchronous CCI functions, it is worth remembering that CCI session connections are all full-duplex, which means that it is permissible to have an outstanding asynchronous CCI-Receive function and an outstanding CCI-Send function operating concurrently on the same session. This is true of all data transmission functions (CCI-Send, CCI-Receive, CCI-Receiveall) which support an asynchronous calling method. It is not permissible, however, to change the state of the session using any of the CCI-Suspend or CCI-Hangup/CCI-Closeclient functions while any asynchronous CCI-Send or CCI-Receive operations are outstanding.

2.3.4 Timeout Handling

When a CCI call returns with a timeout value in the RETURN-CODE, it means that the call took longer than the specified time period to complete.

A timeout can occur as a result of any CCI function call. When a timeout results from a CCI-Query or CCI-Wait function call, it refers to the original CCI function call.

A CCI operation that times out can be re-tried using the same type of call, or you can design your program to take this opportunity to perform some other task.

2.3.5 Tracing and Debugging

The CCI-Trace function enables you to trace CCI function calls made by your application. To trace a call, you must include the CCI-Trace function before the start of the code segment on which a trace is required, setting the CCI-Trace toggle parameter to 1 (enable trace). At the end of the code segment, include another CCI-Trace function call, this time with the toggle parameter set to 0 (disable trace).

The information obtained from the trace is written to a log file which, by default, is called ccitrace.log. In addition to the trace information, CCI data and flow control information can be sent to the log file, depending on the options you specify when calling CCI-Trace.

Although the trace log file is not generally in a readable format, any data output from the CCI data buffers will not be encrypted when placed in this file.

CCI-Trace is not available under DOS. Any call to CCI-Trace from this environment is ignored.

There are two methods of specifying the level of tracing: the CCITRACE environment variable and the control-buffer parameter passed to a CCI-Trace function call.

2.3.5.1 CCITRACE Environment Variable

You can set the CCITRACE environment variable as follows:

CCITRACE=filename [options]

where:

filename is the name of the log file you want the trace output to go to. If you do not specify this parameter, the log file ccitrace.log is created in the current directory with all options set to their default states.
options can be any one, or all, of the following, and must be upper case:

/A or -A logs the status of CCI functions after a call has completed. The default condition for this option is ON unless /B or -B is specified.

/B or -B logs the status of CCI functions before execution. When B is specified, all parameters are logged to the disk file. The default condition for this option is ON unless /A or -A is specified.

/D or -D logs the contents of all buffers passed to and from the CCI functions. The default condition for this option is OFF.

2.3.5.2 The CCI-Trace control-buffer Parameter

The control-buffer parameter is a text string which is passed to a CCI-Trace function call in order to specify the level of tracing required. It can take the same values as those specified for the CCITRACE environment variable above.


Note: The settings of the CCITRACE environment variable overwrite values specified in control-buffer.


See CCI-Trace in the chapter Common Communications Interface for further information.

2.3.6 CCI Function Termination

Every CCI function call must be allowed to terminate before the calling program is canceled or the conversation session is terminated. This condition is automatically satisfied for synchronous operations, but for asynchronous operations it is the caller's responsibility.

All asynchronous function calls must be carried through to completion. Asynchronous functions have completed either if the original call returns a non-zero error code, or when a CCI-Wait or CCI-Query returns a code other than -1. Failure to properly terminate an asynchronous call ties up system resources.

An asynchronous function call is implicitly canceled by closing the session or server handle, and will terminate almost immediately.

Outstanding asynchronous CCI-Connect, CCI-Receiveall, and CCI-Resumeserver requests should be allowed to terminate before executing a CCI-Closeserver for that server handle.

An outstanding asynchronous CCI-Resumeclient request should be allowed to terminate before executing a CCI-Closeclient for that session.

Outstanding asynchronous CCI-Receive and CCI-Send requests should be allowed to terminate before executing a CCI-Closeclient or CCI-Hangup for that session.

2.4 Designing Client/Server Applications

In most client/server architectures, the client initiates communication with a server and, once the connection is established, makes requests to the server using a set of API function calls.

There are three general types of connection between a server and a client:

When designing a client/server application, it is a good idea to use a modular approach, with each module containing common elements of the application. For example, you could construct a module which contains elements which are used by all protocols - such as send and receive. This forms a set of service modules which are called by the main application element. In this way, the elements should be usable in future applications with little or no modification. Porting your application between protocols and operating systems is also made simpler with a good modular design.

2.4.1 Program Segmentation

Both the client and server elements of the basic design can be divided into three areas of operation:

2.4.1.1 Data Transfer

Data is transferred when corresponding send (CCI-Send) and receive (CCI-Receive) functions occur on either side of a conversation between a server and a client.

Once a connection has been established between a client and a server, the client sends data using the CCI-Send function. The server must issue a corresponding CCI-Receive in order to accept the data.

To keep open the connection between the server and the client for multiple data transfers, both the server and client must use corresponding CCI-Send and CCI-Receive functions until all data in that session has been transferred.

Send and receive calls can be made as a single call using CCI-Transact. This function links a single synchronous send with a single synchronous receive in a single call.

2.4.1.2 System Control

System control consists of corresponding pairs of CCI-Resume and CCI-Suspend functions.

A CCI-Resume function must follow each CCI-Init function for data transfer to take place and a CCI-Suspend function must precede each CCI-Close function.

A CCI-Resume function call must precede the data transfer functions, CCI-Send and CCI-Receive, and a CCI-Suspend function call must follow them although it is not necessary to call CCI-Resume and CCI-Suspend each time the data transfer functions are used.

The CCI-Resume and CCI-Suspend functions on the server and client are used to start and stop conversation segments which can be resumed at a later stage. At a lower level, these functions are used by CCI to allocate and release system resources on those protocols where it is possible to do so. This allows as many connections as possible to be sustained from one machine.

For short sessions, you may find it beneficial to call the CCI-Resume and CCI-Suspend functions immediately after a CCI-Init function and before a CCI-Close function. However, for longer sessions, you may find that including several CCI-Resume and CCI-Suspend functions provides a more efficient application.


Note: There is a CCI-Resumeserver call embedded within CCI-Receiveall. A CCI-Resumeserver call should not, therefore, follow a CCI-Receiveall call.


2.4.1.3 Overall Control Area

For a client, the overall control area contains those functions which are used to initialize and close down the client. CCI-Initclient is used to initialize a connection to a specific service and to enable data to be transferred between two corresponding application segments. CCI-Closeclient is used to close down a client connection, following a CCI-Suspendclient function call.

For a server, the overall control area contains the functions used to initialize and close down the server and also those functions which enable data to be received and processed.

Server initialization necessitates several function calls. In a single server/single client connection the sequence of CCI-Initserver, CCI-Connect, CCI-Resume, and CCI-Receive is used. Alternatively, you can simply issue a CCI-Initserver function call followed by CCI-Receiveall.

CCI-Initserver registers the service with the network. CCI-Receiveall can be used to accept incoming connections from new CCI-Initclient calls being made to that server. CCI-Receiveall performs CCI-Resumes on all sessions and returns when data is received on a session connected to this server.

In this way, CCI-Receiveall saves time, particularly in multi-tasking environments such as UNIX and OS/2 where processes might be sharing processor time. A single call to CCI-Receiveall pauses the process until another call to that process is made, or until the call has timed-out.

When you use CCI-Receiveall, you should be aware of the following:

2.5 Setting up a Server and Client

The basic design of a server must include instructions which:

The corresponding client process must include instructions which:

For a single server/single client connection, the CCI functions are performed in the following sequence:

Server
CCI-Initserver
CCI-Connect
CCI-Resumeserver
. . .
. . .
CCI-Receive
<server actions>
CCI-Send
.
.
CCI-Suspendserver
CCI-Hangup
CCI-Closeserver
Client

CCI-Initclient
CCI-Resumeclient

 <client instructions>
CCI-Send

CCI-Receive
 <client actions>
.
CCI-Suspendclient
CCI-Closeclient

In a single server/single client connection, the call to CCI-Connect ensures that only one client is allowed to connect. Once a connection is made, the server stops listening for other clients.

You can also connect the server with many clients, possibly at the same time, using the CCI functions shown below:

Server             Client 1          Client 2
CCIprotocol
CCI-Initserver
                   CCI-Initclient
                   CCI-Resumeclient
CCI-Receiveall     CCI-Send
CCI-Send           CCI-Receive
CCI-Suspendserver  CCI-Suspendclient
                                     CCI-Initclient
                                     CCI-Resumeclient
CCI-Receiveall                       CCI-Send
CCI-Send                             CCI-Receive
CCI-Suspendserver                    CCI-Suspendclient
                   CCI-Resumeclient
CCI-Receiveall     CCI-Send
CCI-Send           CCI-Receive
CCI-Receive        CCI-Send
CCI-Send           CCI-Receive
CCI-Suspendserver  CCI-Suspendclient
CCI-Hangup         CCI-Closeclient
CCI-Hangup                           CCI-Closeclient
CCI-Closeserver

The conversation is initiated at the server by initializing a CCI protocol support module and then calling CCI-Initserver followed by a call to CCI-Receiveall. This call connects with any client requesting a connection, and receives a message from any connected client. The server is not aware of a new connection until the client sends a message which is returned to the server by a CCI-Receiveall call.

2.6 Multi-Protocol Applications

Asynchronous CCI function calls enable you to use more than one communications protocol from a single application. This allows great flexibility in producing network applications that pass data to and from network endpoints using different network protocols.

You can start a service using one protocol (for example, TCP/IP using the CCITCP module), then switch to another (for example, named pipes using the CCINAMP module on OS/2 or the CCINAMPU module on UNIX) and start a service of the same name using the new protocol. You can use any number of protocols, provided that they are supported by your environment.

Switching protocols resets the Linkage Section of your application so that it points to the new protocol. The old protocol retains its own data areas so that you can switch back to it later if you want.

When switching protocols, you must remember the parameters that are specific to each protocol; that is, the srvrhandles, sessionids, async handles, data buffers, and so on.

Example

The following example shows you how to switch from CCITCP to CCINAMP on OS/2. (If you are running under UNIX, substitute CCINAMPU for all occurrences of CCINAMP.)

These instructions correspond to the following code. Note that in this example, only one linkage section record is required.

* Startup application server
* initialize CCITCP
     call "CCITCP" 
     call CCI-Initserver using servername
                               serverhandle-TCP
                               cciend
     call CCI-Receiveall using serverhandle-TCP
                               sessionid-TCP
                               buffer
                               maxlen
                               actuallen
                               async-TCP-recvall
                               cciend
* initialize named pipes support
     call "CCINAMP"  
     call CCI-Initserver using servername
                               serverhandle-NAMP
                               cciend
     call CCI-Receiveall using serverhandle-NAMP
                               sessionid-NAMP
                               buffer
                               maxlen
                               actuallen
                               async-NAMP-recvall
                               cciend
* reset linkage section to TCP/IP support
     call "CCITCP"  
* act on received data if present
     call CCI-Query using async-TCP-recvall
* reply, if needed here
     call CCI-Send using sessionid-TCP
                         buffer
                         sendlen
                         async-TCP-send
                         cciend
     call CCI-Suspendserver using sessionid-TCP
* reset linkage section to named pipes support
     call "CCINAMP" 
     call CCI-Query using async-NAMP-recvall
      .  .  .  

So, in the above example, one service is registered on two network media with the same name. Either of these can be used by calling the relevant CCI function to select the correct protocol support module for use. You do not have to change the Initserver/Receiveall code if a single set of data is used for all the parameters and then stored away in the relevant area for the protocol concerned after the CCI functions are completed.

Further calls to each server and any sessions connected to each server simply require a call to the relevant CCI function to reset the linkage section table. Any or all functions are then available as if they were in a single protocol application.

By using procedure-pointers (function-pointers) to access CCI, you can re-use the transport code to gain access to several protocols without changing execution code.

2.7 Example Program

The following example code makes an initialization call to the CCITCP module and then calls the CCI-Initclient function. This code is contained in the sample programs cciserv.cbl and ccicli.cbl in your cobol\demo directory on DOS, Windows and OS/2 and in the directory $COBDIR/demo/ccitcp on UNIX.

 data division.
 working-storage section.
 01 param-block.
    03 signature.
       05 CCISIGN               pic x(6).
       05 CCITYPE               pic x(8).
       05 CCIVERSN              pic x(6).
    03 table-pointer            usage pointer.
 01 srvrn                       pic x(8) value "server1".
 01 machinename                 pic x(8) value spaces.
 01 sessid                      pic x(4) comp-5.

 linkage section.
 copy "ccitab.cpy".
 01 proctab.
    03 CCI-Initserver           procedure-pointer.
    03 CCI-Closeserver          procedure-pointer.
    03 CCI-Initclient           procedure-pointer.
    03 CCI-Closeclient          procedure-pointer.
    03 CCI-Hangup               procedure-pointer.
    03 CCI-Send                 procedure-pointer.
    03 CCI-Receive              procedure-pointer.
    03 CCI-Receiveall           procedure-pointer.
    03 CCI-Transact             procedure-pointer.
    03 CCI-Connect              procedure-pointer.
    03 CCI-Wait                 procedure-pointer.
    03 CCI-Query                procedure-pointer.
    03 CCI-Resumeclient         procedure-pointer.
    03 CCI-Suspendclient        procedure-pointer.
    03 CCI-Resumeserver         procedure-pointer.
    03 CCI-Suspendserver        procedure-pointer.
    03 CCI-Geterror             procedure-pointer.
    03 CCI-Trace                procedure-pointer.
    03 CCI-Timeout              procedure-pointer.
procedure division.
    call "CCITCP" using param-block

    if ccitype not = "TCP/IP"
        display "TCP/IP expected. CCITCP returned "
        ccitype
        stop run
    end-if
    set address of proctab to table-pointer
    call CCI-Initclient using srvrname
                              machinename
                              sessid
                              async
                              cciend.


Copyright © 1999 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.

PreviousIntroduction to Communications Programming Common Communications InterfaceNext"