Using Callbacks


In this section you modify your examples from previous lessons to use the most common types of callbacks available in TIBCO SmartSockets.

Writing a Process Callback

To see a callback function in action, you define a message process callback function to operate on the incoming NUMERIC_DATA message. As discussed earlier, one of the most commonly used callback types is the process callback. Process callback functions are a common way in TIBCO SmartSockets to perform the main processing of a message. Subject callbacks are another common way of processing messages.

This section describes a callback function in detail. This callback function, which is called when a message of type NUMERIC_DATA, is processed with TipcSrvMsgProcess or with TipcSrvMainLoop, simply accesses and prints the fields of the message.

Step 1

Copy the receive.c program

Copy the receive.c program into your working directory. Under Windows, you also need the makefile recvw32m.mak in your working directory. Use the command chmod 777 * to allow write access to the sample programs you copy.

The contents of the file receive.c are:

  /* receive.c - print out contents of NUMERIC_DATA message via  
                    callback */ 
   /* $RTHOME/examples/smrtsock/tutorial/lesson4/receive.c */ 
 
1   #include <rtworks/ipc.h> 
2   static void T_ENTRY ProcessNumData(T_IPC_CONN conn,  
              T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg); 
 
    /* =========================================================== */ 
    /*..ProcessNumData - callback for processing NUMERIC_DATA  
                          messages */  
3   static void T_ENTRY ProcessNumData 
   ( 
     T_IPC_CONN conn,  
     T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg 
   ) 
   { 
4    T_IPC_MSG msg = data->msg; 
5    T_STR var_name; 
6    T_REAL8 var_value; 
 
7    TutOut("Received NUMERIC_DATA message\n"); 
 
     /* Set current field to first field in message */ 
8    TipcMsgSetCurrent(msg, 0); 
 
     /* Traverse Message; print each name-value pair out */ 
9    while (TipcMsgNextStrReal8(msg, &var_name, &var_value)) { 
10   TutOut("Var Name = %s; Value = %s\n", var_name,  
            TutRealToStr(var_value)); 
   } 
 } /* ProcessNumData */ 
 
   /* ============================================================ */ 
11 int main(int argc, char **argv) 
   { 
12   T_OPTION option; 
13   T_IPC_MT mt; 
14   T_IPC_MSG msg; 
 
     /* Set the project name */ 
15   option = TutOptionLookup("project"); 
16   TutOptionSetEnum(option, "smartsockets"); 
 
     /* Connect to the RTserver */ 
17   if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
18     TutOut("Could not connect to RTserver!\n"); 
19     TutExit(T_EXIT_FAILURE); 
     } 
 
     /* Setup callback to catch NUMERIC_DATA messages */ 
20   mt = TipcMtLookupByNum(T_MT_NUMERIC_DATA); 
21   TipcSrvProcessCbCreate(mt, ProcessNumData, NULL); 
 
     /* Start subscribing to the "/tutorial/lesson4" subject */ 
22   TipcSrvSubjectSetSubscribe("/tutorial/lesson4", TRUE); 
 
     /* Read and process a message */ 
23   msg = TipcSrvMsgNext(T_TIMEOUT_FOREVER); 
24   TipcSrvMsgProcess(msg); 
 
     /* Disconnect from RTserver */ 
25   TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
 
  } /* main */ 

The last command, TipcSrvDestroy, not only disconnects from the RTserver, but also destroys the callback you created, so you don’t need to use TutCbDestroy.

The first thing to notice is that the processing of the message has been moved into the callback function, ProcessNumData, lines 3-10. In addition, the callback function prototype on line 2 and definition on line 3 are both declared with the macro T_ENTRY. This is to assure cross-platform portability. A call to the function is also added in line 24 in the main program to invoke the callback when it is time to process the message.

Step 2

Compile and link the receiving program

Under Windows, copy the makefile recvw32m.mak into your working directory. Compile and link receive.c as you did in the previous lesson.

Step 3

Start the RTserver

Start RTserver if it is not already running:

UNIX:
$ rtserver 
OpenVMS:
$ run rtserver 
Windows:
$ rtserver 

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

Step 4

Start the receiving program

Start the receiving program in one window of your display:

UNIX:
$ receive.x 
OpenVMS:
$ run receive 
Windows:
$ receive 

Step 5

Copy the send.c program

In another window, copy the send.c program into your working directory. Under Windows, you also need the makefile sendw32m.mak in your working directory.

Step 6

Compile and link the sending program

Compile and link the sending program as you did before.

Step 7

Start the sending program

Run the sending program:

UNIX:
$ send.x 
OpenVMS:
$ run send 
Windows:
$ send 

You should see this output in the window where you ran the receiving program:

Received NUMERIC_DATA message 
Var Name = X; Value = 10 
Var Name = Y; Value = 20 
Var Name = Z; Value = 30 

Writing a Default Process Callback

In the previous section, the example was set up to invoke a callback when a message needs to be processed. What happens if you send a message that is not of type NUMERIC_DATA? Next you try it and find out.

Step 8

Copy the send2.c program

Copy the send2.c program into your working directory. Under Windows, copy the makefile snd2w32m.mak into your working directory.

That program is the equivalent of modifying the send.c program by adding this call to TipcSrvMsgWrite after connecting to RTserver and before creating the NUMERIC_DATA message:

TipcSrvMsgWrite("/tutorial/lesson4", TipcMtLookupByNum(T_MT_INFO), 
                TRUE, T_IPC_FT_STR, "Hello World!", NULL); 

This new code sends an INFO message to your receive program, followed by a NUMERIC_DATA message.

Note that in the next two steps, you run receive with send2 instead of the usual pairing of receive with send or receive2 with send2.

Step 9

Start the receiving program

In one window, start the receiving program:

UNIX:
$ receive.x 
OpenVMS:
$ run receive 
Windows:
$ receive 

Step 10

Compile and link the send2.c program

To send a message to the receiving program, compile and link the send2.c program.

Step 11

Start the second sending program

Run the second sending program in another window, which sends an INFO and a NUMERIC_DATA message:

UNIX:
$ send2.x 
OpenVMS:
$ run send2 
Windows:
$ send2 

You do not see any output in the window where you ran the receiving program because the INFO message was received before the NUMERIC_DATA message. Because there was no callback created to process a message of type INFO, the message was ignored. The second message was sent, but because the receiving program is set up to read and process only one message, the NUMERIC_DATA message was never received.

Step 12

Copy the receive2.c program

Copy the receive2.c program into your working directory. Under Windows, copy the makefile rcv2w32m.mak into your working directory.

It contains the receive.c program and has been modified so that it can read and process any number of messages. Copying this file is the equivalent of replacing lines 23 and 24 of receive.c with these three pieces of code:

/* Read and process all incoming messages */ 
while ((msg = TipcSrvMsgNext(T_TIMEOUT_FOREVER)) != NULL) { 
  TipcSrvMsgProcess(msg); 
  TipcMsgDestroy(msg); 
} 

This code creates a while loop that continues to read and process messages until TipcSrvMsgNext returns NULL. Note that each time through the loop destroys the message after processing it.

Now you should create a default process callback to process any non-NUMERIC_DATA messages by adding this code to the receiving program after the callback for NUMERIC_DATA messages has been created:

/* Setup default callback to process anything but NUMERIC_DATA    
   messages */ 
TipcSrvDefaultCbCreate(DefaultReceiveFunc, NULL); 

To complete the program, the default process callback function DefaultReceiveFunc should be added. This function simply prints out the name and type of the message. The changes have been made in the receive2.c program:

/* receive2.c - print out contents of NUMERIC_DATA messages via callback */ 
/* $RTHOME/examples/smrtsock/tutorial/lesson4/receive2.c */ 
 
#include <rtworks/ipc.h> 
 
static void T_ENTRY ProcessNumData (T_IPC_CONN conn, 
           T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg); 
 
static void T_ENTRY DefaultReceiveFunc(T_IPC_CONN conn, 
           T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg); 
/* ================================================================ 
*/ 
/*..ProcessNumData - callback for processing NUMERIC_DATA messages */  
static void T_ENTRY ProcessNumData 
( 
  T_IPC_CONN conn,  
  T_IPC_CONN_PROCESS_CB_DATA data,  
  T_CB_ARG arg 
) 
{ 
  T_IPC_MSG msg = data->msg; 
  T_STR var_name; 
  T_REAL8 var_value; 
 
  TutOut("Received NUMERIC_DATA message\n"); 
 
  /* Set current field to first field in message */ 
  TipcMsgSetCurrent(msg, 0); 
 
  /* Traverse Message; print each name-value pair out */ 
  while (TipcMsgNextStrReal8(msg, &var_name, &var_value)) { 
    TutOut("Var Name = %s; Value = %s\n", var_name,  
           TutRealToStr(var_value)); 
  } 
} /* ProcessNumData */ 
 
/* ================================================================ 
*/ 
/*..DefaultReceiveFunc - default callback */ 
static void T_ENTRY DefaultReceiveFunc 
( 
T_IPC_CONN conn,  
T_IPC_CONN_PROCESS_CB_DATA   data,  
T_CB_ARG arg 
) 
{ 
  T_IPC_MSG msg = data->msg; 
  T_IPC_MT mt; 
  T_STR name; 
 
  /* Print out the name of the type of the message */ 
  TipcMsgGetType(msg, &mt); 
  TipcMtGetName(mt, &name); 
  TutOut("Receive: unexpected message type name is <%s>\n", name); 
} /* DefaultReceiveFunc */ 
 
 
/* ================================================================ 
*/ 
int main(int argc, char **argv) 
{ 
  T_OPTION option; 
  T_IPC_MT mt; 
  T_IPC_MSG msg; 
 
  /* Set the project name */ 
  option = TutOptionLookup("project"); 
  TutOptionSetEnum(option, "smartsockets"); 
 
  /* Connect to RTserver */ 
  if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
    TutOut("Could not connect to RTserver!\n"); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* Setup callback to catch NUMERIC_DATA messages */ 
  mt = TipcMtLookupByNum(T_MT_NUMERIC_DATA); 
  TipcSrvProcessCbCreate(mt, ProcessNumData, NULL); 
 
  /* Setup default callback to process anything but NUMERIC_DATA  
      messages */ 
  TipcSrvDefaultCbCreate(DefaultReceiveFunc, NULL); 
 
  /* Start subscribing to the "/tutorial/lesson4" subject */ 
  TipcSrvSubjectSetSubscribe("/tutorial/lesson4", TRUE); 
 
  /* Read and Process all incoming messages */ 
  while ((msg = TipcSrvMsgNext(T_TIMEOUT_FOREVER)) != NULL) { 
    TipcSrvMsgProcess(msg); 
    TipcMsgDestroy(msg); 
  } 
 
  /* Disconnect from RTserver */ 
  TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
 
} /* main */ 

Before compiling your updated receive2.c program, copy the send3.c program, which is the send2.c program that was modified to send multiple messages.

Step 13

Copy the send3.c program

Copy the send3.c program into your working directory. Under Windows, copy the makefile snd3w32m.mak into your working directory.

The contents of the send3.c file are:

   /* send3.c - send a INFO and then a series of NUMERIC_DATA  
                   messages */ 
   /* $RTHOME/examples/smrtsock/tutorial/lesson4/send3.c */ 
 
1   #include <rtworks/ipc.h> 
 
2   int main(int argc, char **argv) 
    { 
3     T_OPTION option; 
4     T_IPC_MSG msg; 
5     T_INT4 i; 
 
      /* Set the name of the project */ 
6     option = TutOptionLookup("project"); 
7     TutOptionSetEnum(option, "smartsockets"); 
 
      /* Connect to RTserver */ 
8     if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
9       TutOut("Could not connect to RTserver!\n"); 
10      TutExit(T_EXIT_FAILURE); 
      } 
      /* Send an INFO message */ 
11    TipcSrvMsgWrite("/tutorial/lesson4", 
                      TipcMtLookupByNum(T_MT_INFO),TRUE, 
                      T_IPC_FT_STR, "Hello World!", NULL); 
 
       /* Create a message of type NUMERIC_DATA */ 
12     msg = TipcMsgCreate(TipcMtLookupByNum(T_MT_NUMERIC_DATA)); 
 
       /* Set the destination subject of the message */ 
13     TipcMsgSetDest(msg, "/tutorial/lesson4");         
 
       /* Each time through the loop send a NUMERIC_DATA message with 
          3 values */ 
14     for (i = 0; i < 30; i = i + 3) { 
         /* In order to re-use the message, reset the number of the  
            fields in the message to zero */ 
15       TipcMsgSetNumFields(msg, 0); 
 
         /* Build the message with 3 variable-value pairs */ 
16       TipcMsgAppendStrReal8(msg, "X", (T_REAL8)i); 
17       TipcMsgAppendStrReal8(msg, "Y", (T_REAL8)i + 1); 
18       TipcMsgAppendStrReal8(msg, "Z", (T_REAL8)i + 2); 
 
         /* Send the message */ 
19       TipcSrvMsgSend(msg, TRUE); 
20       TipcSrvFlush(); 
       } 
 
      /* Destroy the message */ 
21    TipcMsgDestroy(msg); 
 
      /* Disconnect from RTserver */ 
22    TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
 
   } /* main */ 

Some things to learn from your new send3 program are:

Lines 14-20
This is a for loop that sends out a series of NUMERIC_DATA messages.
Line 15
The same message is re-used each time; only changing the data part of the message. At the beginning of the loop, TipcSetNumFields resets the data part of the message to have zero fields.

In the next three steps, you run receive2 with send3 instead of the usual pairing of receive2 with send2 or receive3 with send3.

Step 14

Compile and link the receive2.c program

Compile and link the receive2.c program.

Step 15

Start the second receiving program

Start the second receiving program in one window of your display:

UNIX:
$ receive2.x 
OpenVMS:
$ run receive2 
Windows:
$ receive2 

Step 16

Compile and link the new send3.c program

In another window, to send a message to the second receiving program, compile and link the new send3.c program.

Step 17

Start the new sending program

To send a message to the receiving program, run the new sending program:

UNIX:
$ send3.x 
OpenVMS:
$ run send3 
Windows:
$ send3 

You should see this output in the window where you ran the receiving program:

Receive: unexpected message type name is <info> 
Received NUMERIC_DATA message 
Var Name = X; Value = 0 
Var Name = Y; Value = 1 
Var Name = Z; Value = 2 
Received NUMERIC_DATA message 
Var Name = X; Value = 3 
Var Name = Y; Value = 4 
Var Name = Z; Value = 5 
 
/* ... Output omitted here ... */ 
Received NUMERIC_DATA message 
Var Name = X; Value = 27 
Var Name = Y; Value = 28 
Var Name = Z; Value = 29 

When the sending program has completed, notice that the receiving program is still hanging because it is waiting for more messages.

Step 18

Interrupt the receiving program

Type Ctrl-c to interrupt the receiving program.

For each NUMERIC_DATA message received, the callback function ProcessNumData was invoked to print out the contents of the data part of the message. The very first message received was an INFO message. Because there were no process callbacks available for INFO messages, the default process callback, DefaultReceiveFunc, was called, which printed out the type of unexpected message received.

Using the TipcSrvMainLoop Convenience Function

A while loop is added in receive2.c to read and process all incoming messages:

while ((msg = TipcSrvMsgNext(T_TIMEOUT_FOREVER)) != NULL) { 
  TipcSrvMsgProcess(msg); 
  TipcMsgDestroy(msg); 
} 

This entire loop can be replaced by a single call:

TipcSrvMainLoop(T_TIMEOUT_FORVER); 

The TipcSrvMainLoop convenience function receives and processes messages from RTserver by calling TipcSrvMsgNext, TipcSrvMsgProcess, and TipcMsgDestroy over and over. TipcSrvMainLoop is a convenience function that keeps calling TipcSrvMsgNext with the time remaining from timeout until TipcSrvMsgNext returns FALSE. For each message that TipcSrvMainLoop gets, it processes the message with TipcSrvMsgProcess and then destroys the message with TipcMsgDestroy. Use 0.0 for timeout to poll the RTserver connection and catch up on all pending messages that have accumulated. Use T_TIMEOUT_FOREVER for timeout to read and process messages indefinitely. As soon as TipcSrvMsgNext returns FALSE, TipcSrvMainLoop returns. See TipcSrvMainLoop in the TIBCO SmartSockets Application Programming Interface reference for more details.

A modified receive2.c program, which uses TipcSrvMainLoop, is located in the file receive3.c. You can compile, link, and run it with the sending program if you want to verify that it produces the same output as before.

Writing a Subject Callback

Rather than processing a message based on its type, you can process a message based on its destination using subject callbacks. With a subject callback, you can specify a separate function for each subject (or group) of subjects you wish to operate on. When a message arrives at the receiver for the specified subject and is ready to be processed, the callback will be executed. Subject callbacks operate in a manner very similar to process callbacks except the function executed is selected based on the message’s destination, not its type. Just as with process callbacks, one can define a default subject callback to be executed if no callback has been defined for a given subject. (See TipcSrvSubjectDefaultCbCreate in the TIBCO SmartSockets Application Programming Interface for more details.)

To create a subject callback, make a call to TipcSrvSubjectCbCreate like this:

T_CB TipcSrvSubjectCbCreate(subject, mt, func, arg) 

where subject is the callback destination and mt is the message type the callback should be applied to. You can specify a value of NULL for subject or mt to specify all. Subject callbacks are actually a superset of process callbacks as they allow message type and subject callbacks to be mixed; for example, execute this callback when a message of type T arrives on subject S.

Some examples of creating subject callbacks are:

mt = TipcMtLookupByNum(T_MT_INFO); 
 
/* Execute the function subj_cb for any message type which has a 
destination of "/tutorial" */ 
TipcSrvSubjectCbCreate("/tutorial", NULL, subj_cb, NULL); 
 
/* Execute the function subj_cb for any messages of type INFO, 
regardless of the destination */ 
TipcSrvSubjectCbCreate(NULL, mt, subj_cb, NULL); 
 
/* Execute the function subj_cb for any messages of type INFO, 
which have a destination of "/tutorial"*/ 
TipcSrvSubjectCbCreate("/tutorial", mt, subj_cb, NULL); 

In this section you modify the examples used for process callbacks to show how easy it is to use subject callbacks. The following code describes a specific subject callback in detail. This callback function is called when a message is processed which has a destination of /tutorial/lesson4. The callback function, ProcessLesson4, simply gets the type of the messages and then prints the fields of the message.

Step 19

Copy the subject callback program

Copy the subject callback program, subjcbs.c, into your working directory. Under Windows, you also need the makefile subjw32m.mak in your working directory. The contents of the file subjcbs.c are:

    /* subjcbs.c - print out contents of messages processed via  
                      subject callback */ 
    /* $RTHOME/examples/smrtsock/tutorial/lesson4/subjcbs.c */ 
 
1   #include <rtworks/ipc.h> 
 
2   static void T_ENTRY ProcessLesson4(T_IPC_CONN conn, 
            T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg); 
 
   /*============================================================= */ 
   /*..ProcessLesson4 - callback for processing messages published to  
                              the /tutorial/lesson4 subject */ 
3  static void T_ENTRY ProcessLesson4 
   ( 
     T_IPC_CONN conn, 
     T_IPC_CONN_PROCESS_CB_DATA data, 
     T_CB_ARG arg 
   ) 
   { 
4    T_IPC_MSG msg = data->msg; 
5    T_STR mt_name; 
6    T_IPC_MT mt; 
7    T_INT4 mt_num; 
8    T_STR msg_text; 
9    T_STR var_name; 
10   T_REAL8 var_value; 
 
     /* Get the message type and print it out */ 
11   TipcMsgGetType(msg, &mt); 
12   TipcMtGetName(mt, &mt_name); 
13   TutOut("*** Received message of type <%s>.\n", mt_name); 
14   TipcMtGetNum(mt, &mt_num); 
 
15   switch(mt_num) { 
16     case T_MT_INFO: 
17       TipcMsgSetCurrent(msg, 0); 
18       TipcMsgNextStr(msg, &msg_text); 
19       TutOut("Text from message = %s\n", msg_text); 
20       break; 
 
21     case T_MT_NUMERIC_DATA: 
22       TipcMsgSetCurrent(msg, 0); 
23       while (TipcMsgNextStrReal8(msg, &var_name, &var_value)) { 
24         TutOut("Var Name = %s; Value = %s\n", var_name,  
                TutRealToStr(var_value)); 
         } /* while */ 
25       break; 
 
26     default: 
27       TutOut("Unable to process messages of this type!\n"); 
28       break; 
    } /* switch */ 
 
    } /* ProcessLesson4 */ 
 
    /* ====================================================== */ 
29  int main(int argc, char **argv) 
    { 
30    T_OPTION option; 
 
      /* Set the project name */ 
31    option = TutOptionLookup("project"); 
32    TutOptionSetEnum(option, "smartsockets"); 
 
      /* Connect to the RTserver */ 
33    if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
34       TutOut("Could not connect to RTserver!\n"); 
35       TutExit(T_EXIT_FAILURE); 
      } /* if */ 
 
      /* Setup callback to catch all messages sent to  
           the "/tutorial/lesson4 subject" */ 
36    TipcSrvSubjectCbCreate("/tutorial/lesson4", NULL,  
                             ProcessLesson4, NULL); 
 
      /* Start subscribing to the "/tutorial/lesson4" subject */ 
37    TipcSrvSubjectSetSubscribe("/tutorial/lesson4", TRUE); 
 
      /* Read and process all incoming messages */ 
38    TipcSrvMainLoop(T_TIMEOUT_FOREVER); 
 
      /* Disconnect from RTserver */ 
39    TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
 
    } /* main */ 

Some interesting things to learn from your new subjcbs program are:

Lines 3-28
The processing of messages of all types is now in the callback function ProcessLesson4. The function first gets the type of the message and then prints outs the contents based on the type. In effect, you have a simple process (message type) callback within a subject callback.
Line 14
A call is made to TipcMtGetNum so the type of the message can be compared as an integer in a switch statement.
Line 36
A subject callback, ProcessLesson4, is defined for messages arriving with a destination of /tutorial/lesson4.
Line 37
Even though we have defined a subject callback on /tutorial/lesson4, we still need to make sure that the program subscribes to the subject.
Line 38
TipcSrvMainLoop invokes the subject callback whenever a message arrives which has the given destination.

You will now execute the new program using subject callbacks to verify that it works correctly.

Step 20

Compile and link

Compile and link the program.

Step 21

Start the subject callback program

Start the subject callback program in one window of your display:

UNIX:
$ subjcbs.x 
OpenVMS:
$ run subjcbs 
Windows:
$ subjcbs 

Step 22

Start the sending program

Run the sending program from the previous example:

UNIX:
$ send3.x 
OpenVMS:
$ run send3 
Windows:
$ send3 

The sending program sends an INFO message followed by a series of NUMERIC_DATA messages. You should see this output in the window where you ran the subject callback program:

Received message of type <info>. 
Text from message = Hello World! 
Rec*** Received message of type <info>. 
Text from message = Hello World! 
*** Received message of type <numeric_data>. 
Var Name = X; Value = 0 
Var Name = Y; Value = 1 
Var Name = Z; Value = 2 
*** Received message of type <numeric_data>. 
Var Name = X; Value = 3 
Var Name = Y; Value = 4 
Var Name = Z; Value = 5 
 
/* ... Output omitted here ... */ 
 
*** Received message of type <numeric_data>. 
Var Name = X; Value = 27 
Var Name = Y; Value = 28 
Var Name = Z; Value = 29 

When send3 has completed, notice that subjcbs is still hanging; it is waiting for more messages.

Step 23

Interrupt the subject callback program

Type Ctrl-c to interrupt the subject callback program.

For each message received, the callback function ProcessLesson4 was invoked to print out the contents of the data part of the message, regardless of the type of the message.

Specifying a Callback Based on Subject and Message Type

The example in the previous section can be further modified to specify a different subject callback for each of the different message types: INFO and NUMERIC_DATA. This is done by specifying two new callback functions: ProcessInfo and ProcessNumData. In the main program, two calls are required to TipcSrvSubjectCbCreate, one for each of the message types. The complete example is:

#include <rtworks/ipc.h> 
 
static void T_ENTRY ProcessInfo(T_IPC_CONN conn,                                                  
        T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg); 
 
static void T_ENTRY ProcessNumData(T_IPC_CONN conn,                                                  
        T_IPC_CONN_PROCESS_CB_DATA data, T_CB_ARG arg); 
/* ================================================================ 
*/ 
/*..ProcessInfo - callback for processing INFO messages published  
                  to the /tutorial/lesson4 subject */ 
static void T_ENTRY ProcessInfo 
( 
  T_IPC_CONN conn, 
  T_IPC_CONN_PROCESS_CB_DATA data, 
  T_CB_ARG arg 
) 
{ 
  T_IPC_MSG msg = data->msg; 
  T_STR msg_text; 
   
  TutOut("*** Received INFO message\n"); 
  TipcMsgSetCurrent(msg, 0); 
  TipcMsgNextStr(msg, &msg_text); 
  TutOut("Text from message = %s\n", msg_text); 
} /* ProcessInfo */ 
/* ================================================================ 
*/ 
/*..ProcessNumData - callback for processing NUMERIC_DATA messages  
                     published  to the /tutorial/lesson4 subject */ 
static void T_ENTRY ProcessNumData 
( 
  T_IPC_CONN conn, 
  T_IPC_CONN_PROCESS_CB_DATA data, 
  T_CB_ARG arg 
) 
{ 
  T_IPC_MSG msg = data->msg; 
  T_STR var_name; 
  T_REAL8 var_value; 
   
  TutOut("*** Received NUMERIC_DATA message\n"); 
  TipcMsgSetCurrent(msg, 0); 
  while (TipcMsgNextStrReal8(msg, &var_name, &var_value)) { 
    TutOut("Var Name = %s; Value = %s\n", var_name,  
            TutRealToStr(var_value)); 
    } /* while */ 
} /* ProcessNumData */ 
/* =============================================================== 
*/ 
int main(int argc, char **argv) 
{ 
  T_OPTION option; 
  T_IPC_MT mt; 
 
  /* Set the project name */ 
  option = TutOptionLookup("project"); 
  TutOptionSetEnum(option, "smartsockets"); 
 
  /* Connect to the RTserver */ 
  if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
    TutOut("Could not connect to RTserver!\n"); 
    TutExit(T_EXIT_FAILURE); 
  } /* if */ 
 
  /* Setup callback to catch INFO messages sent to  
     /tutorial/lesson4 subject */ 
   TipcSrvSubjectCbCreate("/tutorial/lesson4",  
                          TipcMtLookupByNum(T_MT_INFO), 
                          ProcessInfo, NULL); 
 
  /* Setup callback to catch NUMERIC_DATA messages sent to  
     /tutorial/lesson4 subject */ 
   TipcSrvSubjectCbCreate("/tutorial/lesson4",  
                          TipcMtLookupByNum(T_MT_NUMERIC_DATA), 
                          ProcessNumData, NULL); 
 
  /* Start subscribing to the "/tutorial/lesson4" subject */ 
  TipcSrvSubjectSetSubscribe("/tutorial/lesson4", TRUE); 
 
  /* Read and process all incoming messages */ 
  TipcSrvMainLoop(T_TIMEOUT_FOREVER); 
 
  /* Disconnect from RTserver */ 
  TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
 
} /* main */ 

For more details on mixing subject and message type callbacks, see the TIBCO SmartSockets User’s Guide.

Using Server Create and Destroy Callbacks

Earlier in this lesson, you saw example programs that used process and subject callbacks to work with messages. In this section two new callback types are shown: server create and server destroy. Server create callbacks are executed when an RTclient connects to an RTserver and server destroy callbacks are executed when an RTclient disconnects from an RTserver.

In this section, you execute these callbacks using a simple example. The program, srvcbs, prompts you for a password each time it tries to connect to RTserver. If the password is incorrect, the program is disconnected from RTserver and terminated.

Step 24

Copy the create callback program

Copy the create callback program, srvcbs.c, into your working directory. Under Windows, copy the makefile svcbw32m.mak into your working directory.

The contents of srvcbs.c are:

/* srvcbs.c - shows example uses of server create/destroy callbacks */ 
/* $RTHOME/examples/smrtsock/tutorial/lesson4/srvcbs.c */ 
#include <rtworks/ipc.h> 
#define MAX_PASSWD_LENGTH 20 
 
static void T_ENTRY server_connect(T_IPC_CONN conn,            
        T_IPC_SRV_CREATE_CB_DATA data, T_CB_ARG arg); 
static void T_ENTRY server_disconnect(T_IPC_CONN conn,      
        T_IPC_SRV_CREATE_CB_DATA data, T_CB_ARG arg); 
 
/* ================================================================ 
*/ 
/*..server_connect - ask for password when connecting to RTserver */ 
static void T_ENTRY server_connect 
( 
  T_IPC_CONN conn, 
  T_IPC_SRV_CREATE_CB_DATA data, 
  T_CB_ARG arg 
) 
{ 
  T_STR password_correct = "smartsockets\n"; 
  T_CHAR password_entered[MAX_PASSWD_LENGTH]; 
 
  /* Prompt for password when trying to connect to RTserver */ 
  TutOut("Connecting to RTserver...\n"); 
  TutOut("Please enter password : "); 
  fgets(password_entered, MAX_PASSWD_LENGTH, stdin); 
 
/* If password is correct, let them connect to RTserver,  
     otherwise disconnect from RTserver and exit program */ 
  if (!strcmp(password_entered, password_correct)) { 
    /* Passwords matched */ 
    TutOut("Password accepted!\n"); 
  } else { 
    /* Password did not match */ 
    TutOut("Password is not correct!\n"); 
    TutOut("You are being disconnected from RTserver\n"); 
    TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
    TutExit(T_EXIT_FAILURE); 
  } /* if */ 
 
} /* server_connect */ 
/* ================================================================ 
*/ 
/*..server_disconnect - callback for server destroy events */ 
static void T_ENTRY server_disconnect 
( 
  T_IPC_CONN conn, 
  T_IPC_SRV_CREATE_CB_DATA data, 
  T_CB_ARG arg 
) 
{ 
  TutOut("...Disconnecting from RTserver\n"); 
} /* server_disconnect */ 
/* ================================================================ 
*/ 
int main(int argc, char **argv) 
{ 
  /* Setup server create callback */ 
  TipcSrvCreateCbCreate(server_connect, NULL); 
 
  /* Setup server destroy callback */ 
  TipcSrvDestroyCbCreate(server_disconnect, NULL); 
 
  /* Connect to RTserver */ 
  if (!TipcSrvCreate(T_IPC_SRV_CONN_FULL)) { 
    TutOut("Could not connect to RTserver!\n"); 
    TutExit(T_EXIT_FAILURE); 
  } 
 
  /* Read and process all incoming messages */ 
  while (1) { 
    TipcSrvMainLoop(T_TIMEOUT_FOREVER); 
  } 
 
  /* Disconnect from RTserver*/ 
  TipcSrvDestroy(T_IPC_SRV_CONN_NONE); 
 
} /* main */ 

Some things to learn from your new server callback program are:

Step 25

Compile and link the server create program

Compile and link the server create program.

Step 26

Start the server create program

Start the server create program in one window of your display:

UNIX:
srvcbs.x 
OpenVMS:
$ run srvcbs 
Windows:
$ srvcbs 

The program produces output similar to this:

Connecting to project <rtworks> on <_node> RTserver. 
Using local protocol. 
Message from RTserver: Connection established. 
Start subscribing to subject </_workstation.tibco.com_19365>. 
Connecting to RTserver... 
Please enter password:  

Note that the last two lines of output came from the server create callback. This was executed when the process tried to connect to RTserver for the first time. At this point you are being prompted for a password.

Step 27

Enter the password

Enter smartsockets as the password and press the return key:

Please enter password: smartsockets 

The program produces the text Password accepted!. The program is now successfully connected to RTserver. Let’s manually break the connection to RTserver and see what happens.

Step 28

Stop the RTserver

In another window, stop the RTserver process using a command line argument to the rtserver command:

$ rtserver -stop 

The program produces this new output in the window where you ran srvcbs:

WARNING: Lost connection to RTserver: error code = 10. 
...Disconnecting from RTserver 
Attempting to reconnect to RTserver. 
Connecting to project <rtworks> on <_node> RTserver. 
Using local protocol. 
Could not connect to <_node> RTserver. 
Connecting to project <rtworks> on <_node> RTserver. 
Using tcp protocol. 
Could not connect to <_node> RTserver. 
Attempting auto-start of RTserver. 
. 
. 
. 
Connecting to project <rtworks> on <_node> RTserver. 
Using local protocol. 
Message from RTserver: Connection established. 
Start subscribing to subject </_workstation.tibco.com_19375> 
again. 
Connecting to RTserver... 
Please enter password:  

Stopping the RTserver resulted in a sequence of events happening:

Step 29

Enter an incorrect password

This time, enter an incorrect password:

Please enter password: foo 

You should see output similar to this:

Password is not correct! 
You are being disconnected from RTserver 

In this case, the server create callback disconnected from RTserver and terminated the program. In disconnecting from RTserver, the server destroy callback was executed again. This shows that callbacks can cause other callbacks to be executed.

In addition to being used for process authentication as shown in the srvcbs program, server create callbacks are useful for creating other callbacks (such as server process callbacks) that cannot be created until RTclient is connected to RTserver. See TipcSrvCreateCbCreate in the TIBCO SmartSockets Application Programming Interface for more details.


TIBCO SmartSockets™ Tutorial
Software Release 6.8, July 2006
Copyright © TIBCO Software Inc. All rights reserved
www.tibco.com