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:
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
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
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:
Start up the client program in the second window:
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.
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:
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.
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.
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.
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 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.
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:
In C++, a vacant TipcMt object creates a global callback. For example:
TIBCO SmartSockets™ cxxipc Class Library Software Release 6.8, July 2006 Copyright © TIBCO Software Inc. All rights reserved www.tibco.com |