Working with Connections


This section discusses how to create, access, and destroy a connection. To learn more about working with messages, see Chapter 3, Messages. This example programs show the C++ code required to send messages between two processes through a connection. The programs also show how to use each of the connection callback types using a static member function as a callback. For an example using conventional C functions as callbacks, see the code example in Working With The TipcSrv Class on page 63. There are two parts in this example: a server process and a client process.

The source code file for this example is located in these directories:

UNIX:
$RTHOME/examples/cxxipc 
OpenVMS:
RTHOME:[EXAMPLES.CXXIPC] 
Windows:
%RTHOME%\examples\cxxipc 

Server Source Code

The server waits for an client to connect to it, creates some callbacks, and then loops receiving and processing messages.

The server code is:

// conn_srv.cxx -- connections example RTserver    
 
#include <iostream.h> 
#include <rtworks/cxxipc.hxx> 
 
// ------------------------------------------------------------------- 
// User handler class for various callbacks 
// ------------------------------------------------------------------- 
class MyCallbacks { 
public: 
  MyCallbacks() {} 
  virtual ~MyCallbacks() {} 
 
  static void cb_process_numeric_data(T_IPC_CONN conn, 
                               T_IPC_CONN_PROCESS_CB_DATA data, 
                               T_CB_ARG arg); 
 
  static void cb_default(T_IPC_CONN conn, 
                         T_IPC_CONN_DEFAULT_CB_DATA data, 
                         T_CB_ARG arg); 
 
  static void cb_read(T_IPC_CONN conn,  
                      T_IPC_CONN_READ_CB_DATA data, 
                      T_CB_ARG arg); 
 
  static void cb_queue(T_IPC_CONN conn,  
                       T_IPC_CONN_QUEUE_CB_DATA data, 
                       T_CB_ARG arg); 
 
  static void cb_error(T_IPC_CONN conn, 
                       T_IPC_CONN_ERROR_CB_DATA data, 
                       T_CB_ARG arg); 
 
}; 
 
// ================================================================  
//..MyCallbacks::cb_process_numeric_data -- numeric data callback 
void  
MyCallbacks::cb_process_numeric_data(T_IPC_CONN conn, 
                              T_IPC_CONN_PROCESS_CB_DATA data, 
                              T_CB_ARG arg) 
{ 
  TutOut("Entering cb_process_numeric_data.\n");  
 
  TipcMsg msg(data->msg);  
 
  // set current field to first field in message    
  msg.Current(0); 
  if (!msg.Status()) { 
    TutOut("Could not set current field of message.\n"); 
    return; 
  }   
 
  T_STR name; 
  T_REAL8 value; 
 
  // process all the fields of the message    
  while (1) { 
    msg >> name >> value; 
    if (!msg) { 
      break; 
    } 
    TutOut("%s = %f\n", name, value); 
  } 
 
  // make sure we reached the end of the message    
  if (TutErrNumGet() != T_ERR_MSG_EOM) { 
    TutOut("Did not reach end of message.\n"); 
  } 
 
} // cb_process_numeric_data    
 
 
// ================================================================  
//..MyCallbacks::cb_default -- default callback 
void MyCallbacks::cb_default(T_IPC_CONN conn, 
                             T_IPC_CONN_DEFAULT_CB_DATA data, 
                             T_CB_ARG arg) 
 
{ 
  TutOut("Entering cb_default.\n"); 
 
  TipcMsg msg(data->msg);  
 
  TipcMt mt(msg.Type()); 
  if (!mt) { 
    TutOut("Could not get message type from message.\n"); 
    return; 
  } 
   
 
  T_STR name = mt.Name(); 
  if (!mt) { 
    TutOut("Could not get name from message type.\n"); 
    return; 
  } 
 
  TutOut("Message type name is %s\n", name); 
 
} // cb_default    
 
// ================================================================ 
//..MyCallbacks::cb_read -- read callback 
void MyCallbacks::cb_read(T_IPC_CONN conn,  
                          T_IPC_CONN_READ_CB_DATA data, 
                          T_CB_ARG arg) 
 
{ 
  TutOut("Entering cb_read.\n"); 
  TipcMsg msg(data->msg); 
 
  TipcMsgFile *output = (TipcMsgFile *)arg; 
  *output << msg; 
 
} // cb_read    
 
// ================================================================ 
//..MyCallbacks::cb_queue -- queue callback 
void MyCallbacks::cb_queue(T_IPC_CONN conn,  
                           T_IPC_CONN_QUEUE_CB_DATA data, 
                           T_CB_ARG arg) 
{ 
  TutOut("Entering cb_queue.\n"); 
 
  TipcMsg msg(data->msg); 
 
  if (!msg) { 
    TutOut("MyCallbacks::cb_queue() Error constructing TipcMsg\n"); 
    return; 
  } 
 
  TipcMt mt(msg.Type()); 
  if (!mt) { 
    TutOut("MyCallbacks::cb_queue() Error getting message type\n"); 
    return; 
  } 
 
  T_STR name = mt.Name(); 
 
  TutOut("A message of type %s is being handled\n", name); 
 
  // print out the position of the message being inserted/deleted    
  TutOut("A message of type %s is being %s at position %d.\n", 
         name, data->insert_flag ? "inserted" : "deleted", 
         data->position); 
 
} // cb_queue    
 
// ================================================================ 
//..MyCallbacks::cb_error -- error callback 
void MyCallbacks::cb_error(T_IPC_CONN conn, 
                           T_IPC_CONN_ERROR_CB_DATA data, 
                           T_CB_ARG arg) 
 
{ 
  TutOut("Entering cb_error.\n"); 
  TutOut("The error number is %d\n", data->err_num); 
} // cb_error    
 
 
// ================================================================ 
//..main -- main program 
int main() 
{ 
  TipcMt mt_vacant; 
 
  TutOut("Creating server connection to accept clients on.\n"); 
 
  TipcConnServer accepting_server("tcp:_node:5252"); 
  if (!accepting_server) { 
    TutOut("Could not create server connection for " 
           "accepting clients.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  TutOut("Waiting for client to connect.\n"); 
 
  TipcConn *conn = accepting_server.Accept(); 
 
  if (!conn) { 
    TutOut("Could not accept client.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  // create callbacks to be executed when certain operations occur    
  TutOut("Create callbacks.\n"); 
 
  // create a process callback    
  TipcMt mt(T_MT_NUMERIC_DATA); 
  if (!mt) { 
    TutOut("Could not look up NUMERIC_DATA message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
   
  conn->ProcessCbLookup(mt, 
                        MyCallbacks::cb_process_numeric_data, 
                        NULL); 
   
  // check status flag of the object pointed to by conn 
  if (!*conn) { 
    conn->ProcessCbCreate(mt,  
                          MyCallbacks::cb_process_numeric_data, 
                          NULL); 
    if (!*conn) { 
      TutOut("Could not create NUMERIC_DATA process callback.\n"); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
  // create a default callback     
  conn->DefaultCbLookup(MyCallbacks::cb_default, NULL); 
 
  if (!*conn) { 
    conn->DefaultCbCreate(MyCallbacks::cb_default, NULL); 
 
    if (!*conn) { 
      TutOut("Could not create default callback.\n"); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
  // create a message file to use in read callback    
  TipcMsgFile *msg_file  
    = new TipcMsgFile(stdout, T_IPC_MSG_FILE_CREATE_WRITE); 
 
  // create a read callback    
  conn->ReadCbLookup(mt_vacant, MyCallbacks::cb_read, msg_file); 
 
  if (!*conn) { 
    conn->ReadCbCreate(mt_vacant, MyCallbacks::cb_read, msg_file); 
 
    if (!*conn) { 
      TutOut("Could not create read callback.\n"); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
 
  // create a queue callback    
  conn->QueueCbLookup(mt_vacant, MyCallbacks::cb_queue, NULL); 
 
  if (!*conn) { 
    conn->QueueCbCreate(mt_vacant, MyCallbacks::cb_queue, NULL); 
 
    if (!*conn) { 
      TutOut("Could not create queue callback.\n"); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
  // create an error callback    
  conn->ErrorCbLookup(MyCallbacks::cb_error, NULL); 
 
  if (!*conn) { 
    conn->ErrorCbCreate(MyCallbacks::cb_error, NULL); 
 
    if (!*conn) { 
      TutOut("Could not create error callback.\n"); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
  // begin processing messages    
  TutOut("Read and process all messages.\n"); 
 
  TipcMsg msg;  // message received and processed 
 
  for (;;) { 
    msg = conn->Next(T_TIMEOUT_FOREVER); 
    if (!conn->Status()) { 
      break; 
    } 
 
    conn->Process(msg); 
    if (!conn) { 
      TutOut("Could not process message.\n"); 
    } 
 
    msg.Destroy(); 
  } 
   
   // make sure we reached the end of the data    
  if (TutErrNumGet() != T_ERR_EOF) { 
    TutOut("Did not reach end of data.\n"); 
  } 
 
  delete msg_file; 
  delete conn; 
 
  TutOut("Server process exiting successfully.\n"); 
 
  return T_EXIT_SUCCESS;  // all done    
 
}  // main  

Client Source Code

The client process connects to the server process and sends two messages to the server. The client code is:

// conn_clt.cxx -- connections example client 
 
#include <iostream.h> 
#include <rtworks/cxxipc.hxx> 
 
// A simple user C++ class encapsulating a write callback    
class MyWriteCallback {  
public: 
 
  MyWriteCallback() {} 
  virtual ~MyWriteCallback() {} 
 
  static void cb_write(T_IPC_CONN conn,  
                       T_IPC_CONN_WRITE_CB_DATA data, 
                       T_CB_ARG arg); 
}; 
 
// ================================================================ 
//..MyWriteCallback::cb_write -- write callback 
void MyWriteCallback::cb_write(T_IPC_CONN conn,  
                               T_IPC_CONN_WRITE_CB_DATA data, 
                               T_CB_ARG arg)  
{ 
  TutOut("Entering cb_write.\n"); 
  TipcMsg msg(data->msg); 
 
   // print out the message to the message file    
 
  TipcMsgFile *output = (TipcMsgFile *)arg; 
  *output << msg; 
} // cb_write 
 
// ================================================================ 
//..main -- main program 
int main() 
{ 
  TutOut("Creating connection to server process.\n"); 
 
  TipcConnClient client("tcp:_node:5252"); 
 
  if (!client) { 
    TutOut("Could not create connection to server.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  // create callbacks to be executed when certain operations occur 
  TutOut("Create callbacks.\n"); 
 
  TipcMsgFile *msg_file  
    = new TipcMsgFile(stdout, T_IPC_MSG_FILE_CREATE_WRITE); 
 
  // create write callback    
  TipcMt mt_vacant; 
  client.WriteCbLookup(mt_vacant, MyWriteCallback::cb_write, 
                       msg_file); 
 
  if (!client) { 
    client.WriteCbCreate(mt_vacant, MyWriteCallback::cb_write, 
                         msg_file); 
 
    if (!client) { 
      TutOut("Could not create write callback.\n"); 
      return T_EXIT_FAILURE; 
    } 
  } 
 
 
  TutOut("Constructing and sending a NUMERIC_DATA message.\n"); 
 
  TipcMsg msg(T_MT_NUMERIC_DATA); 
 
  if (!msg) { 
    TutOut("Could not create NUMERIC_DATA message.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  msg << "voltage" << (T_REAL8)33.4534  
      << "switch_pos" << (T_REAL8)0.0 << Check; 
 
  if (!msg) {  
    TutOut("Could not append fields to NUMERIC_DATA message\n"); 
    return T_EXIT_FAILURE; 
  } 
  if (!client.Send(msg)) { 
    TutOut("Could not send NUMERIC_DATA message.\n"); 
  } 
 
  TutOut("Constructing and sending an INFO message.\n"); 
 
  TipcMt mt(T_MT_INFO); 
  if (!mt) { 
    TutOut("Could not look up INFO message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!msg.Type(mt)) { 
    TutOut("Could not set message type.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!msg.NumFields(0)) { 
    TutOut("Could not set message num fields.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  msg << "Now is the time"; 
 
  if (!msg) { 
    TutOut("Could not append fields to INFO message.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!client.Send(msg)) { 
    TutOut("Could not send INFO message.\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  if (!client.Flush()) { 
    TutOut("Could not flush buffered outgoing messages\n"); 
    return T_EXIT_FAILURE; 
  } 
 
  delete msg_file; 
 
  TutOut("Client process exiting successfully.\n"); 
  return T_EXIT_SUCCESS;  // all done    
 
}  // main    

Running Server and Client Programs

Compile and link the server and client programs (see Compiling, Linking, and Running on page 13 for generic instructions).

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

Start up the RTserver program in the first window:

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

Start up the client program in the second window:

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

The output from the server program is similar to this:

Creating server connection to accept clients on. 
Waiting for client to connect. 
Create callbacks. 
Read and process all messages. 
Entering cb_read. 
numeric_data _null { 
   voltage 33.4534 
   switch_pos 0 
} 
Entering cb_queue. 
A message of type numeric_data is being handled 
A message of type numeric_data is being inserted at position 0. 
Entering cb_read. 
info _null "Now is the time" 
Entering cb_queue. 
A message of type info is being handled 
A message of type info is being inserted at position 1. 
Entering cb_queue. 
A message of type numeric_data is being handled 
A message of type numeric_data is being deleted at position 0. 
Entering cb_process_numeric_data. 
   voltage = 33.453400 
   switch_pos = 0.000000 
Entering cb_queue. 
A message of type info is being handled 
A message of type info is being deleted at position 0. 
Entering cb_default. 
Message type name is info 
Entering cb_error. 
The error number is 10 
Server process exiting successfully. 

The output from the client program follows.

Creating connection to server process. 
Create callbacks. 
Constructing and sending a NUMERIC_DATA message. 
Entering cb_write. 
numeric_data _null { 
   voltage 33.4534 
   switch_pos 0 
} 
Constructing and sending an INFO message. 
Entering cb_write. 
info _null "Now is the time" 
Client process exiting successfully. 

Creating Connections

As stated in the TIBCO SmartSockets User’s Guide, there are three steps required for two processes to connect to each other in a client-server model:

  1. Creating a server connection.
  1. Creating a client connection.
  2. Accepting the client on the server’s part.

Creating a Server Connection

The server connection, using any one of the supported protocols, is created by using the TipcConnServer constructor.

The TipcConnServer class has two constructors. The first constructor takes a logical connection name as its only parameter. For more information see the TIBCO SmartSockets User’s Guide.

Protocol
Constructor Parameter
Local
local:node:name
where name is a filename
TCP/IP
tcp:node:port_number
a service name may also be used in place of port_number

The only purpose of a server connection is to accept client connections (using the Accept() member function). Messages cannot be sent or received on a server connection.

The Accept() member function returns a pointer to a new TipcConn object, which refers to one end point of a peer-to-peer connection. It only sends and receives messages.

The preferred constructor is the preceding constructor. However, a second constructor is provided in case it is needed in an application under special circumstances. The second constructor takes a C type T_IPC_CONN, which is created with TipcConnCreateServer, as an argument. For example:

server_conn = TipcConnCreateServer("tcp:_node:5252"); 
if (server_conn == NULL) { 
   TutOut("Could not create server connection.\n"); 
   return T_EXIT_FAILURE; 
} 
TipcConnServer server_conn_obj(server_conn, FALSE); 

This constructor may also take an optional second parameter as shown in the preceding example. The second parameter sets a flag inside the object as to whether the connection passed in the first parameter should be destroyed when the object’s destructor is invoked. A value of TRUE is used to destroy the connection when the destructor is invoked, and a value of FALSE is used to leave the connection as-is when the destructor is invoked.

Creating a Client Connection

In a fashion similar to server connections, a client connection is created by using the TipcConnClient constructor:

TipcConnClient conn("tcp:_node:5252"); 
if (!conn) { 
   TutOut("Could not create connection to the server.\n"); 
   return T_EXIT_FAILURE; 
} 

The client connection must use the same IPC protocol as the server. The client connection must be created after the server connection, as the client needs something to contact.

The preferred constructor is the preceding constructor. However, a second constructor is provided in case it is needed in a project under special circumstances. The second constructor takes a C type T_IPC_CONN, which is created with TipcConnCreateClient, as an argument. For example:

conn = TipcConnCreateClient("tcp:_node:5252"); 
if (conn == NULL) { 
   TutOut("Could not create connection to the server.\n"); 
   return T_EXIT_FAILURE; 
} 
TipcConnClient conn_obj(conn, FALSE); 

This constructor may also take an optional second parameter as shown in the example above. The second parameter sets a flag inside the object as to whether the connection passed in the first parameter should be destroyed when the object’s destructor is invoked. A value of TRUE is used to destroy the connection when the destructor is invoked, and a value of FALSE is used to leave the connection as-is when the destructor is invoked.

Accepting the Client

Once the client has constructed a TipcConnClient object to create its connection, the server process must accept the client by calling the member function Accept(). For example:

TipcConn *client_conn = server_conn.Accept(); 
if (client_conn == NULL) { 
   TutOut("Could not accept client.\n"); 
   return T_EXIT_FAILURE; 
} 

Note that the Accept() member function returns a pointer to a new TipcConn object.

Destroying a Connection

In most cases, a connection is destroyed when the managing object’s destructor is called. The only exception to this is when a managing object is created using an existing C type of T_IPC_CONN as the constructor’s first argument and FALSE as the second argument. (This constructor is referred to as the second constructor in the discussions above.)

The C API TipcConnDestroy() function should not be used with any class derived from TipcConn, because TipcConnDestroy() renders TipcConn objects unusable. This happens because TipcConnDestroy() destroys the memory pointed to by the internal T_IPC_CONN pointer of TipcConn. Once that memory is gone, the TipcConn object has no data on which to operate. For example, this code fragment leaves a TipcConnClient object in an unusable state:

TipcConnClient conn("tcp:_node:5252"); 
if (!conn) { 
   TutOut("Could not connect to server\n"); 
   return T_EXIT_FAILURE; 
} 
 
if (!TipcConnDestroy(conn)) { 
   TutOut("Could not destroy connection.\n"); 
} 
 // At this point, conn is unusable  

Callbacks

Callbacks are functions that are executed when certain operations occur. Callbacks are conceptually equivalent to dynamically adding a line of code to a program. For more discussion on callback functions, see the TIBCO SmartSockets Utilities reference.

When the C API to connections is used, a callback function is created using the function whose name takes the form TipcConnTypeCbCreate. A callback function is looked up using the function TipcConnTypeCbLookup and then is manipulated with one of the TutCb* functions.

When the C++ class library is used, callbacks are created by calling a member function of a derived class of TipcConn whose name has the form TypeCbCreate. The code example in Working with Connections on page 43 begins by grouping all the callback functions (cb_process_numeric_data, cb_default, cb_read, cb_queue, and cb_error) together into a single C++ class called MyCallbacks. Callback functions are looked up by using a member function of a derived class of TipcConnWrapper whose name has the form TypeCbLookup.

In general, the derived TipcConn classes do not require callbacks to be C++ class member functions, so you are free to use conventional C functions as callbacks. However, if a C++ member function is to be used as a callback, it must be declared static, as in the example found in Working with Connections on page 43. For an example using conventional C functions as callbacks, see the code example in Working With The TipcSrv Class.

Global Callbacks

In the C API, a global callback is created by passing a NULL in the T_IPC_MT argument of a create callback function. For example, to create a global connection read callback using the C API, do this:

if (!TipcConnReadCbCreate(conn, NULL, my_conn_read_cb, some_data)) 
{ 
   // error    
} 

In C++, a vacant TipcMt object creates a global callback. For example:

TipcMt mt_vacant; 
 
conn.ReadCbCreate(mt_vacant, my_conn_read_cb, some_data); 
if (!conn) { 
   // error    
} 

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