Working With RTclient


This section discusses how to create, access, and destroy connections. Because the focus of this chapter is on the publish-subscribe model, the connections that we describe are usually between the RTclient and an RTserver. To learn more about working with connections in general, see Chapter 2, Connections. The following example programs show the publish-subscribe code used to publish a small data frame of messages, including a user-defined message type, between two RTclients through RTserver. The programs also show how to work with subjects. There are two parts to the example: a sending RTclient and a receiving RTclient. An RTclient can both send messages to and receive messages from RTserver, but this example only shows the two processes doing one or the other. This example only shows a few of the publish-subscribe features built on top of connections.

The source code files for this example are located in the following directory:

UNIX:
$RTHOME/examples/smrtsock/manual  
OpenVMS:
RTHOME:[EXAMPLES.SMRTSOCK.MANUAL] 
Windows:
%RTHOME%\examples\smrtsock\manual 

The online source files have additional #ifdefs to provide C++ support; these #ifdefs are not shown to simplify the example.

Example 14 Common Header Source Code
 
/* rtclient.h -- common header for RTclient examples */ 
 
#define EXAMPLE_PROJECT "example" 
#define EXAMPLE_SUBJECT "rcv" 
#define EXAMPLE_MT_NAME "example_mt" 
#define EXAMPLE_MT_NUM 42 
#define EXAMPLE_MT_GRAMMAR "int4 /*code*/ str /*explanation*/" 
 
void create_ud_msg_types(); 
Example 15 Common Utility Source Code
 
/* rtclutil.c -- RTclient example utilities */ 
 
#include <rtworks/ipc.h> 
#include "rtclient.h" 
 
/* =============================================================== */ 
/*..create_ud_msg_types -- create example user-defined msg types */ 
void create_ud_msg_types() 
{ 
  T_IPC_MT mt; 
 
  /* Create our user-defined message type. */ 
  mt = TipcMtCreate(EXAMPLE_MT_NAME, EXAMPLE_MT_NUM, 
                    EXAMPLE_MT_GRAMMAR); 
  if (mt == NULL) { 
    TutOut("Could not create example message type: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  /* Add the new message type to the DATA logging category. */ 
  if (!TipcSrvLogAddMt(T_IPC_SRV_LOG_DATA, mt)) { 
    TutOut("Could not add example mt to DATA category: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
} /* create_ud_msg_types */ 
Example 16 Receiver Source Code
 
/* rtclrcv.c -- RTclient example receiver */ 
 
/* The receiving RTclient creates its connection to RTserver, */  
/* subscribes to subjects, and receives and processes messages. */ 
 
#include <rtworks/ipc.h> 
#include "rtclient.h" 
 
/* =============================================================== */ 
/*..cb_process_numeric_data -- process callback for NUMERIC_DATA */ 
static void T_ENTRY cb_process_numeric_data( 
                      T_IPC_CONN conn, 
                      T_IPC_CONN_PROCESS_CB_DATA data, 
                      T_CB_ARG arg) 
{ 
  T_STR name; 
  T_REAL8 value; 
 
  TutOut("Entering cb_process_numeric_data.\n"); 
 
  /* set current field to first field in message */ 
  if (!TipcMsgSetCurrent(data->msg, 0)) { 
    TutOut("Could not set current field of message: error <%s>.\n", 
           TutErrStrGet()); 
    return; 
  } 
 
  /* access and print fields */ 
  while (TipcMsgNextStrReal8(data->msg, &name, &value)) { 
    TutOut("%s = %s\n", name, TutRealToStr(value)); 
  } 
  /* make sure we reached the end of the message */ 
  if (TutErrNumGet() != T_ERR_MSG_EOM) { 
    TutOut("Did not reach end of message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
} /* cb_process_numeric_data */ 
 
/* =============================================================== */ 
/*..cb_default -- default callback */ 
static void T_ENTRY cb_default( 
                      T_IPC_CONN conn, 
                      T_IPC_CONN_DEFAULT_CB_DATA data, 
                      T_CB_ARG arg) 
{ 
  T_IPC_MT mt; 
  T_STR name; 
 
  /* This callback function will be called for messages where */ 
  /* there are no process callbacks for that message type. */ 
  TutOut("Entering cb_default.\n"); 
 
  /* print out the name of the type of the message */ 
  if (!TipcMsgGetType(data->msg, &mt)) { 
    TutOut("Could not get message type from message: error 
<%s>.\n", 
           TutErrStrGet()); 
    return; 
  } 
  if (!TipcMtGetName(mt, &name)) { 
    TutOut("Could not get name from message type: error <%s>.\n", 
           TutErrStrGet()); 
    return; 
  } 
  TutOut("Message type name is %s.\n", name); 
} /* cb_default */ 
/* =============================================================== */ 
/*..main -- main program */ 
int main(argc, argv) 
int argc; 
char **argv; 
{ 
    T_OPTION option; 
    T_IPC_MT mt; /* message type for creating callbacks */ 
 
  /* Create user-defined message types. */ 
  create_ud_msg_types(); 
 
  /* Set the option Project to partition ourself. */ 
  option = TutOptionLookup("project"); 
  if (option == NULL) { 
    TutOut("Could not look up option named project: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TutOptionSetEnum(option, EXAMPLE_PROJECT)) { 
    TutOut("Could not set option named project: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* Allow a command-line argument containing the name of a */ 
  /* SmartSockets startup command file. This file can be used */ 
  /* to set options like Server_Names. */ 
  if (argc == 2) { 
    if (!TutCommandParseFile(argv[1])) { 
      TutOut("Could not parse startup command file %s: error 
<%s>.\n", 
             argv[1], TutErrStrGet()); 
      TutExit(T_EXIT_FAILURE); 
    } 
  } 
  else if (argc != 1) { /* too many command-line arguments */ 
    TutOut("Usage: %s [ command_file_name ]\n", argv[0]); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  TutOut("Creating connection to RTserver.\n"); 
  if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
    TutOut("Could not create connection to RTserver: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* create callbacks to be executed when certain operations occur */ 
  TutOut("Create callbacks.\n"); 
 
  /* process callback for NUMERIC_DATA */ 
  mt = TipcMtLookupByNum(T_MT_NUMERIC_DATA); 
  if (mt == NULL) { 
    TutOut("Could not look up NUMERIC_DATA msg type: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (TipcSrvProcessCbCreate(mt, cb_process_numeric_data, NULL) 
      == NULL) { 
    TutOut("Could not create NUMERIC_DATA process cb: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* default callback */ 
  if (TipcSrvDefaultCbCreate(cb_default, NULL) == NULL) { 
    TutOut("Could not create default cb: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  TutOut("Start subscribing to standard subjects.\n"); 
  if (!TipcSrvStdSubjectSetSubscribe(TRUE, FALSE)) { 
    TutOut("Could not subscribe to standard subjects: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  TutOut("Start subscribing to the %s subject.\n", 
EXAMPLE_SUBJECT); 
  if (!TipcSrvSubjectSetSubscribe(EXAMPLE_SUBJECT, TRUE)) { 
    TutOut("Could not start subscribing to %s subject: error 
<%s>.\n", 
           EXAMPLE_SUBJECT, TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* If an error occurs, then TipcSrvMainLoop will restart RTserver */ 
  /* and return FALSE. We can safely continue. */ 
  for (;;) { 
    if (!TipcSrvMainLoop(T_TIMEOUT_FOREVER)) { 
      TutOut("TipcSrvMainLoop failed: error <%s>.\n", 
             TutErrStrGet()); 
    } 
  } 
 
  /* This line should not be reached. */ 
  TutOut("This line should not be reached!!!\n"); 
  return T_EXIT_FAILURE; 
} /* main */ 
Example 17 Sender Source Code
/ 
* rtclsnd.c -- RTclient example sender */ 
 
/* 
This sending RTclient creates its connection and publishes a data frame of messages to a subject 
(through RTserver). 
*/ 
 
#include <rtworks/ipc.h> 
#include "rtclient.h" 
 
/* =============================================================== */ 
/*..main -- main program */ 
int main(argc, argv) 
int argc; 
char **argv; 
{ 
  T_OPTION option; 
  T_IPC_MT mt; 
  /* Create user-defined message types. */ 
  create_ud_msg_types(); 
 
  /* Set the option Project to partition ourself. */ 
  option = TutOptionLookup("project"); 
  if (option == NULL) { 
    TutOut("Could not look up option named project: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TutOptionSetEnum(option, EXAMPLE_PROJECT)) { 
    TutOut("Could not set option named project: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* Log outgoing data messages to a message file. Another */ 
  /* way to set options is to use TutCommandParseStr. */ 
  TutCommandParseStr("setopt log_out_data log_out.msg"); 
 
  /* Allow a command-line argument containing the name of a */ 
  /* SmartSockets startup command file. This file can be used */ 
  /* to set options like Server_Names. */ 
  if (argc == 2) { 
    if (!TutCommandParseFile(argv[1])) { 
      TutOut("Could not parse startup command file %s: error 
<%s>.\n", 
           argv[1], TutErrStrGet()); 
      TutExit(T_EXIT_FAILURE); 
    } 
  } 
  else if (argc != 1) { /* too many command-line arguments */ 
    TutOut("Usage: %s [ command_file_name ]\n", argv[0]); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  TutOut("Creating connection to RTserver.\n"); 
  if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
    TutOut("Could not create connection to RTserver: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  TutOut("Publish a frame of data to the receiver’s subject.\n"); 
 
  TutOut("Publishing a TIME message.\n"); 
  mt = TipcMtLookupByNum(T_MT_TIME); 
  if (mt == NULL) { 
    TutOut("Could not look up TIME msg type: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TipcSrvMsgWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                       T_IPC_FT_REAL8, 1.0, 
                       NULL)) { 
    TutOut("Could not publish TIME message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  TutOut("Publishing a NUMERIC_DATA message.\n"); 
  mt = TipcMtLookupByNum(T_MT_NUMERIC_DATA); 
  if (mt == NULL) { 
    TutOut("Could not look up NUMERIC_DATA msg type: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TipcSrvMsgWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                       T_IPC_FT_STR, "voltage", 
                       T_IPC_FT_REAL8, 33.4534, 
                       T_IPC_FT_STR, "switch_pos", 
                       T_IPC_FT_REAL8, 0.0, 
                       NULL)) { 
    TutOut("Could not publish NUMERIC_DATA message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  TutOut("Publishing an EXAMPLE message.\n"); 
  mt = TipcMtLookupByNum(EXAMPLE_MT_NUM); 
  if (mt == NULL) { 
    TutOut("Could not look up EXAMPLE msg type: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TipcSrvMsgWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                       T_IPC_FT_INT4, 7, 
                       T_IPC_FT_STR, "Seven is your lucky number", 
                       NULL)) { 
    TutOut("Could not publish EXAMPLE message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  TutOut("Publishing an END_OF_FRAME message.\n"); 
  mt = TipcMtLookupByNum(T_MT_END_OF_FRAME); 
  if (mt == NULL) { 
    TutOut("Could not look up END_OF_FRAME msg type: error 
<%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TipcSrvMsgWrite(EXAMPLE_SUBJECT, mt, TRUE, NULL)) { 
    TutOut("Could not publish END_OF_FRAME message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  /* Each RTclient automatically creates a connection process */ 
  /* callback for CONTROL messages. Use this to send the command */ 
  /* "quit force" to the receiver’s command interface. */ 
  TutOut("Publishing a CONTROL message to stop the 
receiver(s).\n"); 
  mt = TipcMtLookupByNum(T_MT_CONTROL); 
  if (mt == NULL) { 
    TutOut("Could not look up CONTROL msg type: error <%s>.\n", 
           TutErrStrGet()); 
    TutExit(T_EXIT_FAILURE); 
  } 
  if (!TipcSrvMsgWrite(EXAMPLE_SUBJECT, mt, TRUE, 
                       T_IPC_FT_STR, "quit force", 
                       NULL)) { 
    TutOut("Could not publish CONTROL message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  /* Flush the buffered outgoing messages to RTserver. */ 
  if (!TipcSrvFlush()) { 
    TutOut("Could not flush messages to RTserver: error <%s>.\n", 
           TutErrStrGet()); 
  } 
 
  /* Completely disconnect from RTserver. */ 
  if (!TipcSrvDestroy(T_IPC_SRV_CONN_NONE)) { 
    TutOut("Could not destroy connection to RTserver: error 
<%s>.\n", 
           TutErrStrGet()); 
  } 
 
  TutOut("Sender RTclient exiting successfully.\n"); 
  return T_EXIT_SUCCESS; /* all done */ 
} /* main */ 

Compiling, Linking, and Running

To compile, link, and run the example programs, first you must either copy the programs to your own directory or have write permission in the following directory or partitioned datasets (MVS):

UNIX:
$RTHOME/examples/smrtsock/manual  
OpenVMS:
RTHOME:[EXAMPLES.SMRTSOCK.MANUAL] 
Windows:
%RTHOME%\examples\smrtsock\manual 

Step 1

To compile and link the programs, use:

UNIX:
$ rtlink -o rtclrcv.x rtclrcv.c rtclutil.c 
$ rtlink -o rtclsnd.x rtclsnd.c rtclutil.c 
OpenVMS:
$ cc rtclrcv.c, rtclsnd.c, rtclutil.c 
$ rtlink /exec=rtclrcv.exe rtclrcv.obj, rtclutil.obj 
$ rtlink /exec=rtclsnd.exe rtclsnd.obj, rtclutil.obj 
Windows:
$ nmake /f clrvw32m.mak 
$ nmake /f clsnw32m.mak 

On UNIX the rtlink command by default uses the cc command to compile and link. To use a C++ compiler or a C compiler with a name other than cc, set the environment variable CC to the name of the compiler, and rtlink then uses this compiler. For example, these commands could be used to compile and link on UNIX with the GNU C++ compiler g++:

$ env CC=g++ rtlink -o rtclrcv.x rtclrcv.c rtclutil.c 
$ env CC=g++ rtlink -o rtclsnd.x rtclsnd.c rtclutil.c 

Step 2

Start RTserver

Before running the programs, start RTserver. An RTclient will not by default automatically start an RTserver. You can change this behavior by using a start prefix in RTclient’s Server_Names option. For more information, see Start Prefix and Starting and Stopping RTserver.

Use this command to start RTserver on 32-bit platforms:

$ rtserver 

To run the programs, start the receiving process first in one terminal emulator window and then the sending process in another terminal emulator window.

On platforms that support both 32- and 64-bit, use the rtserver64 command to run the 64-bit version of RTserver.

Step 3

Start the receiving program in the first window

UNIX:
$ rtclrcv.x 
OpenVMS:
$ run rtclrcv.exe 
Windows:
$ rtclrcv 

Step 4

Start the sending program in the second window

UNIX:
$ rtclsnd.x 
OpenVMS:
$ run rtclsnd.exe 
Windows:
$ rtclsnd.exe 

Here is an example of the receiving process output:

Creating connection to RTserver. 
Connecting to project <example> on <_node> RTserver 
Using local protocol 
Message from RTserver: Connection established. 
Start subscribing to subject </_node_5415> 
Create callbacks. 
Start subscribing to standard subjects. 
Start subscribing to subject </_node> 
Start subscribing to subject </_all> 
Start subscribing to the rcv subject. 
Entering cb_default. 
Message type name is time. 
Entering cb_process_numeric_data. 
voltage = 33.4534 
switch_pos = 0 
Entering cb_default. 
Message type name is example_mt. 
Entering cb_default. 
Message type name is end_of_frame. 

Here is an example of the sending process output:

Now logging outgoing data-related messages to <log_out.msg> 
Creating connection to RTserver. 
Connecting to project <example> on <_node> RTserver 
Using local protocol 
Message from RTserver: Connection established. 
Start subscribing to subject </_node_5415> 
Publish a frame of data to the receiver’s subject. 
Publishing a TIME message. 
Publishing a NUMERIC_DATA message. 
Publishing an EXAMPLE message. 
Publishing an END_OF_FRAME message. 
Publishing a CONTROL message to stop the receiver(s). 
Now logging outgoing data - related messages to <log_out.msg> 
Sender RTclient exiting successfully. 

The message file log_out.msg, which is created by the sender:

time rcv 1 
numeric_data rcv { 
 voltage 33.4534 
 switch_pos 0 
} 
example_mt rcv 7 "Seven is your lucky number" 
end_of_frame rcv 
control rcv "quit force" 

More than one receiving process can be started if desired. Each receiving process receives the same messages from the sending process. To run three receivers, start the receiving program in three separate windows or in batch (background), using the same command as shown in Step 3.

Include Files

Code written in C or C++ that uses the SmartSockets Application Programming Interface (API) must include the header file <rtworks/ipc.h>. This file is located in these directories or partitioned datasets (MVS):

UNIX:
$RTHOME/include/$RTARCH/rtworks  
OpenVMS:
RTHOME:[INCLUDE.RTWORKS] 
Windows:
%RTHOME%\include\rtworks 

The SmartSockets IPC API includes all the functions used for interprocess communication.

Differences Between the TipcConn* and TipcSrv* API

Most TipcConn* connection functions have an equivalent TipcSrv* function that operates only on the connection to RTserver. For most TipcConn* functions that have a TipcSrv* equivalent, the calling sequences are identical except that the T_IPC_CONN first parameter in the TipcConn* function disappears in the TipcSrv* function because the connection is always with RTserver. For example, the C/C++ function prototype for TipcConnMsgProcess is:

T_BOOL TipcConnMsgProcess(T_IPC_CONN conn, T_IPC_MSG msg); 

The corresponding function prototype for TipcSrvMsgProcess is:

T_BOOL TipcSrvMsgProcess(T_IPC_MSG msg); 

Table 6 shows the TipcSrv* functions that have names similar to TipcConn* functions, but that have different behavior.

Table 6 TipcSrv* Functions With Different Behavior  
Function Name
Difference from Related TipcConn* Function
TipcSrvCreate
TipcConnCreate creates an empty connection, while TipcSrvCreate uses several options to find, possibly start, and connect to RTserver.
TipcSrvDestroy
TipcConnDestroy deallocates memory and closes a socket, while TipcSrvDestroy may leave a warm connection to RTserver.
TipcSrvMsgSend
TipcSrvMsgWrite
TipcSrvMsgWriteVa
These functions set the sender property of a message to the value in the option Unique_Subject and have an additional check_server_msg_send parameter (of type T_BOOL). TipcSrvMsgWrite and TipcSrvMsgWriteVa also have an additional destination parameter (of type T_STR).
TipcSrvGmdFileDelete
Because TipcSrvCreate calls TipcSrvGmdResend (which will open any existing GMD files and thus not allow them to be deleted), TipcSrvGmdFileDelete does not call TipcConnGmdFileDelete but does use the same algorithm.

Table 7 shows the TipcConn* functions that do not have TipcSrv* equivalents.

Table 7 TipcConn* Functions Without TipcSrv* Equivalents 
Function Name
Explanation
TipcConnCreateClient
TipcConnCreateServer
An RTclient uses the function TipcSrvCreate, which uses the options Server_Names and Default_Protocols to determine which IPC protocol to use to create a client connection to RTserver. An RTclient always creates a client connection, not a server connection, when it connects to RTserver.
TipcConnAccept
RTserver accepts a connection from RTclient, not the other way around.
TipcConnEncodeCbCreate TipcConnEncodeCbLookup TipcConnDecodeCbCreate TipcConnDecodeCbLookup
RTclient does not support encode and decode callbacks on the connection to RTserver.

Table 8 shows the TipcSrv* functions that do not have TipcConn* equivalents.

Table 8 TipcSrv* Functions Without TipcConn* Equivalents 
Functions
Explanation
TipcSrvLogAddMt
TipcSrvLogRemoveMt
These functions manipulate standard message file logging types, which do not exist in connections, only in RTclient.
TipcSrvCreateCbCreate TipcSrvCreateCbLookup TipcSrvDestroyCbCreate TipcSrvDestroyCbLookup TipcSrvSubjectCbCreate TipcSrvSubjectCbDestroyAll TipcSrvSubjectCbLookup TipcSrvSubjectDefaultCbCreate TipcSrvSubjectDefaultCbLookup TipcSrvTraverseCbCreate TipcSrvTraverseCbLookup
These functions manipulate server create callbacks, server destroy callbacks, server names traverse callbacks, and subject callbacks, which do not exist in connections, only in RTclient.
TipcSrvSubjectGetSubscribe TipcSrvSubjectSetSubscribe TipcSrvSubjectGetSubscribeLb TipcSrvSubjectSetSubscribeLb TipcSrvSubjectGmdInit TipcSrvSubjectLbInit TipcSrvSubjectTraverseSubscribe
These functions manipulate subjects, which do not exist in connections, only in RTserver and RTclient.
TipcSrvStdSubjectSetSubscribe
TipcSrvStdSubjectTraverse
These functions manipulate standard subjects, which do not exist in connections, only in RTserver and RTclient.
TipcSrvGetConnStatus TipcSrvIsRunning TipcSrvStop
Miscellaneous RTclient utility functions.
TipcSrvGmdMsgServerDelete
TipcSrvGmdMsgStatus
These functions provide GMD that exists in RTclient but not in connections.

Setting Options

Options allow you to customize your RTclient’s configuration. A user-defined RTclient does not have any standard startup command files. It is up to you to decide which command files, if any, should be loaded. Startup command files can be loaded with the function TutCommandParseStr or by using TutCommandParseFile. Options can also be set using the function TutOptionSetType, where Type is replaced with the type of the option. All RTclient options are discussed in detail in Chapter 8, Options Reference.

Creating a Connection to RTserver

After an RTclient has initialized its options, a connection to RTserver can be created. There are two kinds of global connections to RTserver: a warm connection and a full connection. Warm connections are discussed in Warm Connection to RTserver. Through the remainder of this document, the term connection to RTserver is used to mean a full global connection to RTserver.

The function TipcSrvCreate is used to create a connection to RTserver. For example:

if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
  TutOut("Could not create connection to RTserver: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 

The connect command provides a way to use TipcSrvCreate from the SmartSockets command interface (see the command reference for connect).

An RTclient cannot have a global connection to more than one RTserver at a time, although it can move its connection from one RTserver to another RTserver at any time. However, instead of a single global connection, an RTclient can have multiple RTserver connections using a special type of multiple connection. For more information in C, see Connecting to Multiple RTservers. For more information in Java, see the TIBCO SmartSockets Java Library User’s Guide and Tutorial.

TipcSrvCreate uses several options to control the creation of the connection to RTserver:

Default_Protocols
specifies a list of IPC protocols to try if no protocol is specified.
Server_Disconnect_Mode
specifies the action RTserver should take when RTclient disconnects.
Server_Names
specifies a list of logical connection names used to find and start RTserver.
Server_Start_Delay
specifies the number of seconds to sleep between traversals of the Server_Names option.
Server_Start_Max_Tries
specifies the number of times to traverse the Server_Names option.
Server_Start_Timeout
specifies the number of seconds to wait for RTserver to initialize.
Udp_Broadcast_Timeout
specifies the number of seconds to wait for broadcast replies.

The options Default_Subject_Prefix, Project, Server_Disconnect_Mode, and Unique_Subject do not directly control connecting to RTserver, but they are used once RTclient has found an RTserver.

Creating a Connection to RTgms

If the SmartSockets system is enabled for multicast and the RTclient wants to use multicast, the RTclient must connect to an RTgms instead of connecting to an RTserver. In most cases, the only change required is to the Group_Names and Server_Names options in the RTclient command (.cm) file.

The Group_Names option specifies which multicast group the RTclient belongs to. The default is rtworks, and you only need to change the value if you are not using that group name.

The Server_Names option must provide the logical connection name for an RTgms process instead of the logical connection name for an RTserver process.

For example, your RTclient command file might contain:

setopt group_names rtworks 
setopt server_names tcp:nodea  

Let’s assume the RTclient should belong to the multicast group mcast1, and should connect to the RTgms on nodea using the default port, which is 5104. Change the lines to:

setopt group_names mcast1 
setopt server_names pgm:nodea 

If you want to connect to an RTgms that is not using the default port, change the Server_Names line to:

setopt server_names pgm:nodea:tcp.6000 

which connects to the RTgms on nodea using port 6000. For more information on the format of RTgms addresses, see Address for Multicast.

After ensuring your options are set correctly for multicast, use the function TipcSrvCreate to connect to the RTgms process the same way you connect to an RTserver process:

if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
  TutOut("Could not create connection to RTgms: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 

When connecting to an RTgms process, you cannot specify pgm:localhost alone. You must also include TCP as the protocol:

pgm:localhost:tcp 

In addition to the options used in creating connections for an RTclient, there are special options that apply only to multicast connections. The names of these options begin with Pgm_. These options must be set in a multicast command file, mcast.cm. For information on these options and on the multicast command file, see Chapter 10, Using Multicast. If your SmartSockets system does not have the multicast option installed, you receive an error when you attempt to connect to RTgms.

Belonging to a Project

When an RTclient creates a connection to RTserver, it becomes part of the project named in the option Project. As described in Projects, each project is self-contained, and no messages can be sent between projects.

Logical Connection Names for RT Processes

SmartSockets RT processes, such as RTserver and RTclient, simplify the creation of connections with logical connection names that are specified consistently for all protocols. RTserver and RTclient take full advantage of the logical connection name feature of connections. For an introduction to these features, see the section Logical Connection Names. RTserver uses logical connection names to create its connections, and RTclient uses logical connection names to create a client connection to an RTserver or RTgms.

The most important option for connecting to another RT process is Server_Names, which is used to find and start an RT process such as RTserver. Server_Names is a list of logical connection names. Each logical connection name has the form:

protocol:node:address 

which can be shortened to protocol:node, protocol, or simply node for normal connections. The TipcSrvCreate function expands the shortened forms to the long protocol:node:address form and uses default values for the parts that are not specified. For RTclient to find an RTserver, one of the logical connection names used by RTclient must exactly match one of the logical connection names used by that RTserver (for example, the name tcp:abc:1234 does not match the name tcp:xyz:1234).

For RTclient to automatically start or re-start an RTserver, Server_Names must use one of the start prefixes listed in Start Prefix. By default, the start prefix is start_never, meaning that RTclient cannot start RTserver.

Protocol Portion

The protocol part of the connection name refers to an IPC protocol type. Examples of common protocols are tcp, local. There is also the special SmartSockets protocol defined for multicast, pgm. The pgm protocol is only supported if your SmartSockets system has the multicast option installed.

If protocol is omitted from the connection name, then all protocols listed in the Default_Protocols option are tried in order. These protocol names (with the exception of udp_broadcast, which is described in the next section) map to the TipcConnCreateClient function described in the section Creating a Client Connection.

If you specify pgm for multicast as the protocol, the address portion of the logical connection name uses a different format than for other protocols. See Address for Multicast.

The Udp_Broadcast Protocol

The udp_broadcast protocol is only used to find RTserver, and not to start RTserver or send or receive messages. When the udp_broadcast protocol is used, a packet is broadcast to all nodes on the local network to attempt to find an RTserver. If an RTserver receives the broadcast packet, it responds to the request with a list of logical connection names for RTclient to use to find that RTserver. RTclient waits the number of seconds specified in the Udp_Broadcast_Timeout option for RTserver to respond to its broadcast, and then connects to the RTserver that responds first.

There are several categories of UDP broadcast addresses that vary in how wide an area they are distributed over. The book TCP/IP Illustrated, Volume 1: The Protocols by W. Richard Stevens has an excellent discussion of broadcasting. The categories are shown below.

Unfortunately, these categories behave differently depending on the operating system and network router configuration used, making it hard to pick a default broadcast address that works in all situations. The limited and all-subnets-directed broadcast addresses do not work on several operating systems supported by SmartSockets.

Therefore, by default the broadcast packet is sent to the subnet-directed address of the main network interface. In non-subnetted networks, the net-directed, subnet-directed, and all-subnet-directed broadcast addresses are equivalent, which makes the subnet-directed address the most reasonable default for most networks.

When using the udp_broadcast protocol, a dotted-decimal address can be used for the node portion of the logical connection name to override the broadcast default. For example, the logical connection name udp_broadcast:255.255.255.255 can be used on many platforms as a limited broadcast address.

SmartSockets does not support broadcast over several network interfaces at the same time.

Node Portion

The node part of the connection name refers to a computer node name. If node is omitted from the connection name, then _node, the current node, is used as a default. Using the default node works if your RTserver or RTgms process to which you are connecting is on the same node as your RTclient. If you configured them to be on a different node, you must specify that node for the node portion of the connection name.

Address Portion

The address part of the connection name refers to a protocol-specific IPC location, such as a tcp port number. If address is omitted from the connection name, a protocol-specific default address is used. The default addresses for all protocols are:

Protocol Name
Default Address
local
RTSERVER
pgm
unicast_protocol.address
tcp
5101
udp_broadcast
5101

Address for Multicast

If you are connecting to an RTgms process for multicast, instead of to an RTserver, the address portion of your logical connection name is a different format than for other protocols. The format for multicast is:

unicast_protocol.address 

where:

unicast_protocol
specifies the unicast protocol to use when sending data to an RTgms. The valid values you can specify are tcp or local.
This field is optional, unless you specified localhost for the node on a UNIX system. If you specify localhost for the node, the unicast protocol must be tcp.
On Windows, the default is tcp.
On UNIX, where the default is local, you must specify tcp as the unicast protocol:
  
pgm:localhost:tcp 
address
specifies the address portion of the unicast logical connection name used by the RTgms to receive data. This is the address or port defined for the RTgms. The default is 5104.
This field is optional.

If you specify a multicast format address, and your SmartSockets system does not have the multicast option installed, you receive an error when you attempt to connect to RTgms.

Start Prefix

Each logical connection name in RTclient can also have a logical connection name modifier called a start prefix at the front, which must be separated from the name with a colon:

start_prefix:protocol:node:address 

For more information, see Logical Connection Name Modifiers.

The start prefix controls if and when an RTclient tries to start RTserver. On Windows, if an RTserver has been installed as a Windows service, you must also set the proper environment variable before an RTclient can use a start prefix to start that RTserver. Set the RTSERVER_CMD environment variable to:

net start "SmartSockets RTserver" 

The valid start prefixes are:

start_always
RTclient always tries to start RTserver if it cannot create the connection to RTserver.
start_on_demand
RTclient only tries to start RTserver if the RTclient has tried all names in Server_Names at least once. This is useful for only starting RTserver if an existing one cannot be found. For this start prefix to be used, the Server_Start_Max_Tries option must be increased above the default value of 1 (see pseudo code in Finding and Starting RTserver for details).
start_never
RTclient never tries to start RTserver. This is the default.

If no start prefix is specified, an implicit default start prefix of start_never is used. If the protocol portion of the logical connection name is local and the node portion is not the name of the current node, an implicit start prefix of start_never is used because the local protocol cannot connect to a remote RTserver.

The start prefix is only for an RTclient to start an RTserver. It cannot be used by any other process to start an RTserver or used by an RTclient to start a process other than an RTserver.

Randomizing Server_Names

The special value _random can be used in the Server_Names option in an RTclient to randomize the list of RTserver names. Every name occurring after _random is tried in a random order. This enables load balancing of the RTclient connections to the associated RTservers. For example, if the option Server_Names is set to workstation1, _random, workstation2, workstation3, workstation4, then workstation1 is always tried first, and then workstation2, workstation3, and workstation4 are tried in a random order each call to the TipcSrvCreate function.

Scanning Server_Names

The special value _next can be used in the server_names option for an RTclient. This special value changes the starting point at which the RTclient traverses the server_names option in the event of server failure. If included, the _next value must be the first value presented in the server_names list. Each server name is tried in sequential order, starting with the "next" server listed after the currently connected server. This prevents the RTclient from attempting to reconnect to a server to which it had previously lost its connection.

For example:

setopt server_names _next, server1, server2, server3, server4 

When the RTclient initially tries to make a connection to a server, it attempts to connect to server1. Once a connection is established to server1, should a failure occur in server1, the RTclient attempts reconnection by starting with the "next" server in the list, server2. Without the _next property, the RTclient would always attempt to connect to the list of servers starting with the first server in the list. The _next option does not eliminate any servers from the list; if the RTclient reaches the end of the list when using the _next option, then the RTclient circles back around to the front of the list and continues connection attempts until all servers in the list have been attempted.

RTservers with Multiple IP Addresses

Generally, for an RTclient to find an RTserver, the logical connection name used in Server_Names by the RTclient must exactly match the logical connection name used in Conn_Names by the RTserver. For example, the name tcp:moe:1234 does not match the name tcp:conan:1234. However, this perfect match is not needed when the RTclient is trying to find an RT server that is listening on all IP addresses on a machine with more than one IP address, that is, multi-homed.

An RTserver listens on all IP addresses of a machine if the node portion of its logical connection name (specified in Conn_Names) is _any. Using _node for node causes the RTserver to listen only on the default IP address. For example, if an RTserver is on a machine with IP addresses ipaddr_a and ipaddr_b, and its Conn_Names is set to tcp:_any:5101, an RTclient can connect to this RTserver if its Server_Names option is set to either tcp:ipaddr_a:5101 or tcp:ipaddr_b:5101.

If you want the RTserver to listen only on a specific IP address, you can specify that IP address in the Conn_Names option. However, there is a known issue that occurs if running an RTserver on a machine with multiple IP addresses when using udp_broadcast to discover RTservers. Any responding RTserver replies with only its default IP address, even if its Conn_Names option is specifically set to listen on an IP address other than the default. If you are using udp_broadcast, be sure to include the default IP address in the RTserver Conn_Names option, or use _node or _any in the Conn_Names option.

Using the keyword _any in Conn_Names is discouraged for RTserver to RTserver connections. When an RTserver connects to another RTserver whose Conn_Names use _any, the RTserver might attempt to reconnect every Server_Reconnect_Interval seconds. This is a known problem and will be fixed in a future release.

Finding and Starting RTserver

An RTclient traverses the list of names in Server_Names at most Server_Start_Max_Tries. Between each traversal RTclient sleeps for Server_Start_Delay seconds. Each time an RTclient tries to start RTserver, it waits for up to Server_Start_Timeout seconds for that RTserver to finish initializing. For each entry in Server_Names, RTclient tries to connect, then possibly tries to start RTserver (depending on what start prefix is used), then tries to connect again.

There is a server names traverse callback available to RTclients. This callback is executed before each attempt to connect to an RTserver in the Server_Names list. For more information, see Server Names Traverse Callbacks.

This pseudo code shows the algorithm used by TipcSrvCreate:

randomize Server_Names list if _random is used 
 
for (each $try from 1 to Server_Start_Max_Tries) { 
 
  for (each $name in Server_Names) { 
    connect to RTserver using $name 
    if (connection could be created) { 
      if (no connection previously existed) { 
        resend old GMD messages 
      } 
      execute server create callbacks 
      return 
    } 
    else { 
      if ($name has the start prefix "start_always" 
          or $name has no start prefix 
          or ($name has the start prefix "start_on_demand" 
              and $try > 1)) { 
        start RTserver with command "rtserver -node node" 
        wait up to Server_Start_Timeout seconds for RTserver to init 
        connect to RTserver using $name 
        if (connection could be created) { 
          if (no connection previously existed) { 
            resend old GMD messages 
          } 
          execute server create callbacks 
          return 
        } 
      } /* if we should try to start RTserver */ 
    } /* if no connection */ 
  } /* for each name */ 
 
  sleep for Server_Start_Delay seconds 
} /* for each try */
The start_on_demand start prefix only starts RTserver if the option Server_Start_Max_Tries is set to a value larger than 1 (the default).
 

If TipcSrvCreate is able to successfully create a connection, it calls the server create callbacks (see RTclient-Specific Callbacks for information on server create callbacks).

The options shown in the example are only accessed when the RTclient creates a connection to RTserver. If one of these options is changed, it does not take effect until the next time the RTclient creates a connection to RTserver. See Changing RTclient Options for an example of how to change these options and have them take effect immediately.

Starting RTserver on a Remote Node

When RTclient starts RTserver, it uses the node portion of the logical connection name to determine which node to attempt to start RTserver on. To start RTserver on a remote node, it must be possible to perform a remote shell rsh (remsh on HP-UX) to that node without a password. If rsh to the remote system does not work, then the RTclient is not able to start RTserver. Use this command to test rsh (on all platforms except HP-UX):

$ rsh node rtserver -help 

On HP-UX, use this command:

$ remsh node rtserver -help 

On platforms that support both 32- and 64-bit, use the rtserver64 command to run the 64-bit version of RTserver.

If a prompt for a password appears when this command is entered, contact the system administrator for help in configuring the remote shell (rsh or remsh) to not require a password.

Starting RTserver on a remote machine under OpenVMS requires a TCP/IP package running with support for the rsh command, such as MultiNet.

Local Protocol Failure

Occasionally, you may find that RTclient can no longer connect to RTserver using the local protocol, but can still connect using the TCP protocol. For example:

Connecting to project <rtworks> on <_node> RTserver. 
Using local protocol. 
connect: No such file or directory 
Could not connect to <_node> RTserver. 
Connecting to project <rtworks> on <_node> RTserver. 
Using tcp protocol. 
Message from RTserver: Connection established. 

One possible scenario is the local socket file used by RTserver (stored in the directory specified by the function TutGetSocketDir) gets deleted somehow, perhaps by an overzealous system administrator. To fix the problem, simply restart RTserver, and a new socket file is created.

Automatically Connecting to RTserver

The above example programs explicitly call the function TipcSrvCreate to create a connection to RTserver. RTclient can also automatically create a connection to RTserver if one is needed, such as when RTclient tries to publish a message to RTserver before it has created a connection to RTserver. Most of the TipcSrv* functions automatically attempt to create a connection to RTserver (by calling TipcSrvCreate themselves) if one is needed and the option Server_Auto_Connect is set to TRUE.

The standard SmartSockets modules also use Server_Auto_Connect in a second way. During initialization, each of these standard processes uses Server_Auto_Connect to determine whether or not to require a connection to RTserver. If Server_Auto_Connect is TRUE, the process requires a connection to RTserver, and creates one, if necessary, to start up. To run the standard processes without any connection to RTserver, Server_Auto_Connect must be set to FALSE. Some standard data sources in the standard SmartSockets processes also require a connection to RTserver regardless of the value of Server_Auto_Connect.

Table 9 shows the TipcSrv* functions that do not automatically create a connection to RTserver

Table 9 TipcSrv* Functions That do not Automatically Create a Connection to RTserver 
Function Name
Explanation
TipcSrvLogAddMt TipcSrvLogRemoveMt
The standard message file logging types can only be manipulated before a connection to RTserver is created.
TipcSrvCreateCbCreate TipcSrvCreateCbLookup TipcSrvDestroyCbCreate TipcSrvDestroyCbLookup
Manipulating server create callbacks and server destroy callbacks does not need a connection to RTserver.
TipcSrvCreate TipcSrvDestroy
These functions explicitly manipulate the connection to RTserver.
TipcSrvGetConnStatus TipcSrvIsRunning
TipcSrvStop
Miscellaneous RTclient utility functions.
TipcSrvGmdFileDelete
Because TipcSrvCreate calls TipcSrvGmdResend, which will open any existing GMD files and thus not allow them to be deleted, TipcSrvGmdFileDelete cannot create a connection to RTserver

.

If a user-defined RTclient does not care when it creates a connection to RTserver, it never needs to call TipcSrvCreate and can just let the process automatically connect to RTserver when necessary. However, it is usually a good idea to explicitly connect to RTserver once all the necessary options have been set to the desired values.

Automatically Reconnecting to RTserver

When an unrecoverable error occurs on the connection to RTserver, the connection error callbacks for this connection are called (see Error Callbacks, for more details on connection error callbacks). The standard connection error callback function TipcCbSrvError normally takes care of reconnecting to RTserver. TipcCbSrvError uses the function TipcSrvCreate to reconnect to RTserver so that the same options are used to both connect and reconnect to RTserver. Any messages that were being buffered to be sent to RTserver are lost, but RTclient is able to continue. If the RTclient cannot reconnect to RTserver, it keeps a warm connection to RTserver in the hope that it is able to reconnect to RTserver in the near future. For more information on TipcCbSrvError and how it is automatically created as a connection error callback, see the man page for TipcCbSrvError in the TIBCO SmartSockets Application Programming Interface reference.

While most subject and monitoring information is kept in RTserver, RTclients do keep track of the subjects they are subscribing to and monitoring categories they are watching, so that when they reconnect to RTserver, they can easily start subscribing to those subjects and watching those monitoring categories again.

If several RTclients are connected to RTserver and they all have errors occur on their connections to RTserver (for example, RTserver fails or the node RTserver is running on fails), it is possible that all of the RTclients might try to restart RTserver. These many simultaneous restart attempts can use large amounts of network resources. Avoid this when you configure the option Server_Names through the use of the start prefixes (discussed in Start Prefix), by ensuring that only one or two of the RTclients ever tries to restart RTserver. The default behavior is for RTclients to not attempt to restart the RTserver.

Destroying the Connection to RTserver

If an RTclient is done communicating with RTserver, it can destroy its connection to RTserver. The connection can be fully destroyed, which destroys all RTserver-related information in an RTclient, or it can be partially destroyed, which leaves a subset of the full information in a warm connection. Warm connections are discussed in Warm Connection to RTserver. From this point on, the term destroying the connection to RTserver is used to mean fully destroying the connection to RTserver.

The function TipcSrvDestroy is used to destroy the connection to RTserver. For example:

if (!TipcSrvDestroy(T_IPC_SRV_CONN_NONE)) { 
  TutOut("Could not destroy connection to RTserver: error <%s>.\n", 
         TutErrStrGet()); 
} 

The disconnect command provides a way to use TipcSrvDestroy from the SmartSockets command interface (see the man page for disconnect in Chapter 9, Command Reference, for more details). After a process destroys its connection to RTserver it can continue as if it had never been connected to RTserver.

For a full destroy, TipcSrvDestroy removes all local subject information, destroys all internal callbacks (for options like Server_Read_Timeout and Log_In_Data), destroys the connection, and finally calls the server destroy callbacks (see RTclient-Specific Callbacks on server destroy callbacks). All messages that have been sent to the connection to RTserver but not flushed are lost after a call to TipcSrvDestroy. If a warm connection is retained, all messages that have been read from the connection to RTserver but not processed after the call to TipcSrvDestroy(T_IPC_SRV_CONN_WARM) are still available.

TipcSrvDestroy also attempts to send a DISCONNECT message to notify RTserver of the value of the option Server_Disconnect_Mode. DISCONNECT messages are discussed in DISCONNECT Message Type.

An RTclient using the TCP protocol to connect to RTserver can lose outgoing messages if the process terminates without calling TipcSrvDestroy. TCP/IP’s SO_LINGER option, which preserves data, is ignored when closing a socket that has non-blocking I/O enabled. While data loss is rare on UNIX, MVS, and OpenVMS, it can happen frequently on Windows. TipcSrvDestroy sets the block mode of the connection to FALSE before closing the connection’s socket, forcing the operating system to deliver all flushed outgoing messages.

Using Subjects

Subscribing to a Subject

The function TipcSrvSubjectSetSubscribe can be used to start or stop subscribing to a subject. This code causes RTclient to start subscribing to the subject named /elec_pwr:

if (!TipcSrvSubjectSetSubscribe("/elec_pwr", TRUE)) { 
  TutOut("Could not start subscribing to the /elec_pwr 
           subject.\n"); 
  TutOut("  error <%s>.\n", TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 

Once RTclient starts subscribing to a subject, it receives all messages sent to RTserver that have that subject as their destination property. If the second parameter to TipcSrvSubjectSetSubscribe is FALSE, then RTclient stops subscribing to the subject. TipcSrvSubjectSetSubscribe causes a SUBJECT_SET_SUBSCRIBE message to be sent to RTserver, but the message is not automatically flushed. See Sending Messages for a discussion of sending and buffering messages to RTserver.

The function TipcSrvSubjectGetSubscribe can be used to determine whether or not RTclient is subscribing to a subject. For example:

T_BOOL subscribe_status; 
 
if (!TipcSrvSubjectGetSubscribe("/elec_pwr", &subscribe_status)) { 
  /* error */ 
} 
 
TutOut("This process %s subscribing to the /elec_pwr subject.\n", 
       subscribe_status ? "is" : "is not"); 

The subscribe and unsubscribe commands provide a way to use TipcSrvSubjectSetSubscribe from the SmartSockets command interface. See subscribe and unsubscribe.

Monitoring a Subject

There are many types of information about subjects that can be monitored. For information on monitoring, see Chapter 5, Project Monitoring.

Unique Subject

As described in Unique Subject, an RTclient has a unique subject, which RTserver requires to be unique among all processes in a project. This code shows how an RTclient can use this property to ensure that it is the only process subscribing to a subject:

TutCommandParseStr("setopt unique_subject my_subject"); 
if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL) 
    && TutErrNumGet() == T_ERR_SRV_ACCESS_DENIED) { 
  TutOut("Another RTclient is using my_subject!\n"); 
} 

Because the unique subject must be unique among all processes in a project, it can be used to prevent multiple similarly-configured processes from running (whether accidentally or intentionally). Because the default value for the unique subject is generated from the node name and process identifier of the RTclient, the default value is usually adequate for RTclients that aren’t using guaranteed message delivery. Configuring GMD discusses how Unique_Subject must be explicitly set to use file-based GMD.

RTclient automatically subscribes to its unique subject when it connects to RTserver, and SmartSockets does not allow RTclient to ever stop subscribing to its unique subject.

Standard Subjects

The function TipcSrvStdSubjectSetSubscribe can be used to start or stop subscribing to the standard subjects. This code causes RTclient to start subscribing to all standard subjects, including the _time subject:

if (!TipcSrvStdSubjectSetSubscribe(TRUE, TRUE)) { 
  /* error */ 
} 

If the first parameter to TipcSrvStdSubjectSetSubscribe is FALSE, then RTclient stops subscribing to the standard subjects. If the second parameter to TipcSrvStdSubjectSetSubscribe is FALSE, then the _time subject is not included with the standard subjects.

Subject Callbacks

Subject callbacks are functions that are executed while processing a message with the given destination (that is, subject). Subject callbacks are similar to connection process callbacks, but have added flexibility to filter the callbacks based on destination and message type. The functions TipcSrvSubjectCbCreate and TipcSrvSubjectDefaultCbCreate can be used to create subject callbacks. The following code causes RTclient to create a subject callback that gets executed when any type of message is received by the "/stocks" subject:

if (!TipcSrvSubjectCbCreate("/stocks", NULL, subject_cb, NULL)) { 
  /* error */ 
} 

See RTclient-Specific Callbacks for more information on this subject callbacks.

Callbacks

Once RTclient has connected to RTserver, it can create callbacks to be executed when certain operations occur. Most of the connection callback types, except encode and decode callbacks, are available with the connection to RTserver. These connection callback types are discussed in detail in the section Callbacks. In addition to the connection callbacks, subject, server create, server destroy, and server names traverse callbacks are available for the connection to RTserver.

For the connection to RTserver, callbacks are manipulated with TipcSrv* functions instead of TipcConn* functions. The TipcSrv*CbCreate functions create callbacks in the connection to RTserver, and the TipcSrv*CbLookup functions look up existing callbacks.

This creates a process callback called whenever a NUMERIC_DATA message is processed:

mt = TipcMtLookupByNum(T_MT_NUMERIC_DATA); 
if (mt == NULL) { 
  TutOut("Could not look up NUMERIC_DATA msg type: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
}  
if (TipcSrvProcessCbCreate(mt, cb_process_numeric_data, NULL) 
  == NULL) { 
  TutOut("Could not create NUMERIC_DATA process cb: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 

The callback functions themselves are written exactly the same for the connection to RTserver as for any other connection. The following code shows a callback function that prints the values in a NUMERIC_DATA message. The definition and prototype for every callback function must use the T_ENTRY macro for cross-platform compatibility.

/* =============================================================== */ 
/*..cb_process_numeric_data -- process callback for NUMERIC_DATA */ 
static void T_ENTRY cb_process_numeric_data( 
                      T_IPC_CONN conn, 
                      T_IPC_CONN_PROCESS_CB_DATA data, 
                      T_CB_ARG arg) 
{ 
  T_STR name; 
  T_REAL8 value; 
 
  TutOut("Entering cb_process_numeric_data.\n"); 
 
  /* set current field to first field in message */ 
  if (!TipcMsgSetCurrent(data->msg, 0)) { 
    TutOut("Could not set current field of message: error <%s>.\n", 
           TutErrStrGet()); 
    return; 
  } 
  /* access and print fields */ 
  while (TipcMsgNextStrReal8(data->msg, &name, &value)) { 
    TutOut("%s = %s\n", name, TutRealToStr(value)); 
  } 
  /* make sure we reached the end of the message */ 
  if (TutErrNumGet() != T_ERR_MSG_EOM) { 
    TutOut("Did not reach end of message: error <%s>.\n", 
           TutErrStrGet()); 
  } 
} /* cb_process_numeric_data */ 

These callbacks can be created either before or after RTclient starts working with subjects, but the callbacks should be created before any messages are sent or received (if the callbacks are needed for those messages).

RTclient also has callbacks that can be called when certain RTclient-specific events occur, such as when the connection to RTserver is created. See RTclient-Specific Callbacks for more information on these callback types.

Receiving and Processing Messages

RTclient receives and processes messages from the connection to RTserver the same way that non-RTserver connections are used. See Receiving and Processing Messages, for more details. As with callbacks, the TipcSrv* functions are used, not the TipcConn* functions.

For example:

/* If an error occurs, then TipcSrvMainLoop will restart RTserver */ 
/* and return FALSE. We can safely continue. */ 
for (;;) { 
  if (!TipcSrvMainLoop(T_TIMEOUT_FOREVER)) { 
    TutOut("TipcSrvMainLoop failed: error <%s>.\n", 
           TutErrStrGet()); 
  } 
} 

The above loop handles the case where RTclient loses its connection to RTserver and reconnects again, and simply prints some diagnostic output each time this occurs. Each program can add more or less error checking as desired.

Processing CONTROL Messages

When RTclient creates a connection to RTserver, the function TipcSrvCreate also creates a connection process callback for CONTROL messages using the standard callback function TipcCbSrvProcessControl. This callback function uses the value of the option Enable_Control_Msgs to check if the command is enabled, and if allowed, RTclient then calls TutCommandParseStr to have the process command interface execute the command. This automatic processing of CONTROL messages allows RTclient to publish remote commands to any other RTclient. See Enable_Control_Msgs for details on how to properly configure CONTROL message security.

For more information on TipcCbSrvProcessControl and how it is automatically created as a connection process callback, see the reference page for TipcCbSrvProcessControl in the TIBCO SmartSockets Application Programming Interface reference.

Sending Messages

RTclient sends (publishes) messages using the connection to RTserver in a way that is similar, but not quite identical, to the way other connections are used. The functions TipcSrvMsgSend, TipcSrvMsgWrite, and TipcSrvMsgWriteVa differ from their TipcConn* relatives in these ways:

The Server_Msg_Send option specifies whether or not RTclient should send messages to RTserver. If check_server_msg_send is TRUE and Server_Msg_Send is FALSE, then the above TipcSrv* functions do not send the message. Some messages sent internally by the SmartSockets IPC library, such as SUBJECT_SET_SUBSCRIBE messages, are always sent regardless of the setting of Server_Msg_Send. This option is useful for backup processes that should receive messages from RTserver but not send any out. The use of any option greatly simplifies the development of such backup processes; for a complete example see Running an RTclient With a Hot Backup. You should normally use TRUE for the check_server_msg_send parameter to the above TipcSrv* functions if you wish to use the Server_Msg_Send option to easily create backup processes.

This uses TipcSrvMsgWrite to send an INFO message to all processes in a project:

TutOut("Publishing a CONTROL message to stop the receiver(s).\n"); 
mt = TipcMtLookupByNum(T_MT_CONTROL); 
if (mt == NULL) { 
  TutOut("Could not look up CONTROL msg type: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 
  if (!TipcSrvMsgWrite("_ie", mt, TRUE, 
                       T_IPC_FT_STR, "quit force", 
                       NULL)) { 
  TutOut("Could not publish CONTROL message: error <%s>.\n", 
         TutErrStrGet()); 
} 

The previous example could be rewritten as follows to use TipcSrvMsgSend instead of TipcSrvMsgWrite:

TutOut("Publishing a CONTROL message to stop the receiver(s).\n"); 
mt = TipcMtLookupByNum(T_MT_CONTROL); 
if (mt == NULL) { 
  TutOut("Could not look up CONTROL msg type: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 
msg = TipcMsgCreate(mt); 
if (msg == NULL) { 
  TutOut("Could not create CONTROL message: error <%s>.\n", 
         TutErrStrGet()); 
  TutExit(T_EXIT_FAILURE); 
} 
if (!TipcMsgSetDest(msg, "_ie") 
      || !TipcMsgAppendStr(msg, "quit force") 
      || !TipcSrvMsgSend(msg, TRUE)) { 
  TutOut("Could not construct and publish CONTROL message.\n"); 
  TutOut("  error <%s>.\n", TutErrStrGet()); 
} 

Buffering of Outgoing Messages

The connection to RTserver has the same auto flush size property that all other connections have for controlling how many bytes of outgoing data are buffered before being automatically flushed. This property can be set with the function TipcSrvSetAutoFlushSize, but it can also be accessed with the Server_Auto_Flush_Size option. This option can be set in startup command files for greater convenience in all RTclients.


TIBCO SmartSockets™ User’s Guide
Software Release 6.8, July 2006
Copyright © TIBCO Software Inc. All rights reserved
www.tibco.com