Advanced Uses of Connections


The previous section described the most common ways of working with connections. This section shows some advanced ways of using connections.

Mixing Connections and Xt Intrinsics (Motif)

Connections can be used in a Motif (or any other Xt-based widget set) program through the use of the Xt Intrinsics functions XtAppAddInput and XtRemoveInput. Both Motif and connections have their own main loop functions: XtAppMainLoop for Motif and TipcConnMainLoop for connections. The easiest way to mix these two main loops is to use XtAppAddInput to add the connection as a source of input into Xt’s event-handling mechanism and then use XtAppMainLoop. One of the parameters to XtAppAddInput is the function to be called when data is available. When data is available for reading on the connection, XtAppMainLoop calls your user-defined function, which can call TipcConnMainLoop. If TipcConnMainLoop fails, then XtRemoveInput should be called to remove the connection as a source of input. Refer to your operating system manuals for full information on the functions XtAppAddInput, XtAppMainLoop, and XtRemoveInput.

The function TipcConnGetXtSource can be used to get an XtAppAddInput-compatible source from a connection. On UNIX, this source is the socket file descriptor of the connection (that is, TipcConnGetXtSource gets the same thing as TipcConnGetSocket). On OpenVMS, this source is an event flag in cluster zero, which is needed for the OpenVMS implementation of XtAppAddInput.

This code fragment shows how to use XtAppAddInput to register a function to be called when data is available for reading on a connection:

/* =============================================================== */ 
/*..xt_conn_func -- data available on a connection */ 
void xt_conn_func(client_data, source, id) 
XtPointer client_data; /* really (T_IPC_CONN) */ 
int *source; 
XtInputId *id; 
{ 
  T_IPC_CONN conn = (T_IPC_CONN)client_data; 
 
  /* Process all messages that can be read immediately. */ 
  if (!TipcConnMainLoop(conn, 0.0)) { 
    /* an error occurred, so remove the source of input */ 
    XtRemoveInput(*id); 
    /* additional error handling goes here */ 
  } 
} /* xt_conn_func */ 
/*...code from calling function is below */ 
 
T_INT4 xt_source; 
 
if (!TipcConnGetXtSource(conn, &xt_source)) { 
  /* error */ 
} 

Use TipcConnMainLoop, or TipcConnMsgNext in a loop here instead of just calling TipcConnMsgNext once. Otherwise, messages might be read in but left in the connection message queue where XtAppMainLoop cannot see them. Timeout values other than 0.0 should also be used with caution here, as a non-zero timeout can cause the process to wait for data and decrease the responsiveness of the Motif/Xt user interface.

XtAppAddInput(app_context, 
              xt_source, 
#ifdef T_OS_VMS    /* needed by OpenVMS XtAppAddInput */ 
              NULL, 
#else 
              (XtPointer)XtInputReadMask, 
#endif 
              xt_conn_func, 
              (XtPointer)conn); 

The previous example uses TipcConnMainLoop(conn, 0.0) to read and process all messages that are immediately available on the connection. If your program cannot immediately process all messages for some reason, such as it can only process one message and then must allow the Motif/Xt user interface to be updated, there are many Motif/Xt features available:

These Motif/Xt features can be used in a wide variety of combinations, and the best combination depends on the specific needs of the program you are developing. Refer to your operating system manuals for full information on the functions XtAppAddWorkProc, XtAppAddTimeOut, XtAppPending, and XtAppProcessEvent.

As described above, using connections with XtAppAddInput requires special care for the buffering of incoming messages. Extra effort is also needed sometimes to ensure proper buffering of outgoing messages. TipcConnMsgSend sends a message but may not flush the message. Likewise many TipcMon* and TipcSrvSubject* functions send messages to RTserver but do not explicitly flush these messages. This can cause problems because XtAppMainLoop does not know to flush these messages, and often no incoming messages arrive until these buffered outgoing messages are flushed. The easiest solutions to this problem are to set the connection auto flush size property to 0 or to call TipcConnFlush after sending messages but before returning from your Motif/Xt callback functions.

In addition to checking when data is available to be read from a connection, XtAppAddInput can also be used on a server connection to check when a client has connected and can be immediately accepted with TipcConnAccept.

The source code for a complete example of mixing SmartSockets and Motif is located in these files:

UNIX:
$RTHOME/examples/smrtsock/motifipc.c 
OpenVMS:
RTHOME:[EXAMPLES.SMRTSOCK]MOTIFIPC.C 
Windows:
%RTHOME%\examples\smrtsock\motifipc.c 

Mixing Connections and the Select Function

The select function, which is often associated with sockets but can be used with any file descriptor on most UNIX systems, provides a way to check many file descriptors without polling each one individually. The function TutSocketCheck provides a simplified interface to the select function that works well if you only have one file descriptor. For example, the function TipcConnCheck uses TutSocketCheck because a connection has exactly one socket. Many UNIX systems also have a poll function that is similar to select, and the techniques described in this section can be used for poll as well as select.

If you need to check multiple connections, or a mixture of sockets and connections, then you must call select directly. Refer to your operating system manuals for full information on the function select. The function TipcConnGetSocket can be used to get the connection’s socket file descriptor, which can then be used with select.

This code fragment shows how to use select to wait for up to 10 seconds for data to be available for reading on a connection or a socket:

fd_set read_set; 
T_INT4 conn_fd; 
T_INT4 largest_fd; 
struct timeval time_out; 
int status; 
 
/* get the socket file descriptor from the connection */ 
if (!TipcConnGetSocket(conn, &conn_fd)) { 
  /* error */ 
} 
 
/* set the appropriate bits in the read set */ 
FD_ZERO(&read_set); 
FD_SET(conn_fd, &read_set); 
FD_SET(socket_fd, &read_set); 
 
largest_fd = (conn_fd > socket_fd) ? conn_fd : socket_fd; 
time_out.tv_sec = 10; /* 10 seconds */ 
time_out.tv_usec = 0; /* 0 microseconds */ 
 
status = select(largest_fd + 1, 
                &read_set, 
                NULL, 
                NULL, 
                &time_out); /* use NULL to wait indefinitely */ 
TutOut("%d file descriptors are ready for reading.\n", status); 
 
if (FD_ISSET(conn_fd, &read_set)) { 
  TutOut("Connection is ready for reading.\n"); 
  if (!TipcConnMainLoop(conn, 0.0)) { 
    /* error */ 
  } 
}
Use TipcConnMainLoop, or TipcConnMsgNext in a loop, here instead of just calling TipcConnMsgNext once. Otherwise, messages might be read in but left in the connection message queue where select cannot see them. Timeout values other than 0.0 should also be used with caution here, as a non-zero timeout can cause the process to wait for data and thus decrease the response time to other processing needs.
 
if (FD_ISSET(socket_fd, &read_set)) { 
  TutOut("Socket is ready for reading.\n"); 
  /* call recv or read */ 
} 

In addition to checking when data is available to be read from a connection, select can also be used on a server connection to check when a client has connected and can be immediately accepted with TipcConnAccept.

Mixing Connections and the Windows Message Loop

At the heart of all Windows applications is a message processing loop. Within this loop, the application checks for the arrival of a Windows message. When one arrives it is processed. The typical call used to check for incoming messages is GetMessage. This is a blocking Windows call, and if no message is waiting to be processed, the application will block until a message arrives. To provide timely response to user-generated events, your application must service pending event messages in a timely manner. Interfacing SmartSockets with Windows requires careful coding to ensure that the user interface is serviced in a timely manner while still providing the real-time response required for your application.

Many of the SmartSockets function calls block until a specific condition is met, such as a SmartSockets message arriving. You should be careful not to use these blocking calls in the Windows event-driven environment. Using them can result in your application appearing to be non-responsive to user input, or even, in the case of Win16, locking up the entire system. All of the blocking SmartSockets calls have corresponding non-blocking calls that can be used in an event driven environment.

WSAAsyncSelect is a function provided by winsock.dll, the Windows Socket Library. It provides a means for receiving notification when a SmartSockets message arrives on a connection. The service is activated by passing the connection’s socket handle along with an indication of what events are to be notified; in this case you want to know when data is ready to be read. The SmartSockets function TipcConnGetSocket returns the connection’s socket handle. A Windows message number and HWND window handle to notify are also passed to the function. When data is ready to be read from the socket, a message is sent to your window.

The message number passed to the function should be a user-defined message, based on WM_USER. The socket notification remains in force until explicitly cancelled. This code fragment shows how to use WSAAsyncSelect to wait for data to be available for reading on a connection:

#define MY_SOCKET_MSG (WM_USER + 0) 
 
T_IPC_CONN conn; 
 
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, 
                     LPSTR lpszArgs, int nWinMode) 
{ 
  HWND hwnd 
  SOCKET sock; 
 
  /* Define a window class */ 
  ... 
 
  /* Register the window class *. 
  ... 
 
  /* Create the window */ 
  hwnd = CreateWindow(... 
 
  /* Create client connection */ 
  conn = TipcConnCreateClient(... 
 
  /* get the connection’s socket */ 
  if (!TipcConnGetSocket(conn, &sock)) { 
    /* error */ 
  } 
 
  /* set up for read notification */ 
  if (WSAAsyncSelect(sock, hwnd,  
                        MY_SOCK_MSG, FD_READ) == SOCKET_ERROR) { 
    /* error */ 
  } 
 
  /* main message loop */ 
  ... 
} 
 
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, 
                                WPARAM wParam, LPARAM lParam) 
{ 
  switch (message) { 
    case ... 
    case MY_SOCK_MSG: 
      if (!TipcConnMainLoop(conn, 0.0)) { 
        /* error */ 
      } 
      break; 
    default: ... 
    } 
} 

In addition to checking when data is available to be read from a connection, WSAAsyncSelect can also be used on a server connection to check when a client has connected and can be immediately accepted with TipcConnAccept.

The source code for a complete example of mixing SmartSockets and the Windows Microsoft Foundation Classes (MFC) is located in these directories:

UNIX:
$RTHOME/examples/smrtsock/windows/rtwcon 
OpenVMS:
RTHOME:[EXAMPLES.SMRTSOCK.WINDOWS.RTWCON] 
Windows:
%RTHOME%\examples\smrtsock\windows\rtwcon 

Remote Procedure Calls

Messages can be sent between connections to perform a remote procedure call. A remote procedure call (RPC) is a means for a process to execute a function in another process and wait for the result of the function call. Normally when a process sends a message through a connection with TipcConnMsgSend, the sending process continues and does not wait for the receiving process to receive the message and act on it. This normal mode of operation can be thought of as a non-blocking RPC that does not return a result.

The function TipcConnMsgSendRpc performs a blocking RPC that does return a result. One message is sent as the RPC call from the caller end of the connection, and another message is sent back as the RPC result to the caller. The callee end of the connection must be prepared to receive the call message and send back the result message. TipcConnMsgSendRpc uses a simple relationship between the call and result messages: the message type number of the result message must be one greater than the message type number of the call message.

See the reference information for TipcConnMsgSendRpc in the TIBCO SmartSockets Application Programming Interface for a code example of how to perform RPCs with connections.

Time Resolution

Many connection properties, such as read timeout, and API functions, such as TipcConnMsgNext, have a time-related value associated with them. Most SmartSockets time-related values are stored in a T_REAL8 eight-byte real number. A T_REAL8 has a limited precision of approximately 15 significant digits. Many of the time-related values are further constrained by the resolution provided by the select function and the resolution of the operating system internal clock. The select function uses a timeval structure similar to the SmartSockets T_TIME_STRUCT structure; both have two four-byte integer fields for fixed second and microsecond resolution. Most operating systems do not provide microsecond resolution, however. See Mixing Connections and the Select Function for more information on using connections with select. Time-related values can always use fractional seconds but the resolution of the fractional part varies depending on your configuration.

File Descriptor Upper Limit

All operating systems supported by SmartSockets have an upper limit on the number of file descriptors and socket descriptors a process can use simultaneously. On most UNIX-based operating systems the setrlimit function is used to raise the per-process descriptor limit. On OpenVMS the FILLM quota controls the per-process descriptor limit. Refer to your operating system manuals for more information on configuring descriptor limits to larger values (sometimes the operating system itself must be reconfigured).

The size of the fd_set socket data structure controls how many socket descriptors can be used with the select function, regardless of the setting of the per-process descriptor limit. The fd_set data structure is used by TutSleep, TutSocketCheck, RTlm, and RTserver. The size of fd_set is by default defined in an operating system C language header file, is compiled into SmartSockets, and cannot be changed at run-time.

On most operating systems supported by SmartSockets, fd_set is large enough to accommodate at least 1024 simultaneous descriptors. This allows a process like RTserver the potential to manage approximately 1015 RTclient processes at once! On some platforms SmartSockets has direct support for the poll system call. On these platforms the number of descriptors that can be simultaneously managed by rtserver is greatly increased. To support large numbers of client connections in RTserver, the option Max_Client_Conns must be increased above its default value.


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