JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Programming Interfaces Guide     Oracle Solaris 11.1 Information Library
search filter icon
search icon

Document Information

Preface

1.  Memory and CPU Management

2.  Session Description Protocol API

3.  Process Scheduler

4.  Locality Group APIs

5.  Input/Output Interfaces

6.  Interprocess Communication

7.  Socket Interfaces

8.  Programming With XTI and TLI

What Are XTI and TLI?

XTI/TLI Read/Write Interface

Write Data

Read Data

Close Connection

Advanced XTI/TLI Topics

Asynchronous Execution Mode

Advanced XTI/TLI Programming Example

Asynchronous Networking

Networking Programming Models

Asynchronous Connectionless-Mode Service

Making the Endpoint Asynchronous

Asynchronous Network Transfers

Asynchronous Connection-Mode Service

Asynchronously Establishing a Connection

Asynchronous Use of a Connection

Asynchronous Open

Transferring a File Descriptor

State Transitions

XTI/TLI States

Outgoing Events

Incoming Events

State Tables

Guidelines to Protocol Independence

XTI/TLI Versus Socket Interfaces

Socket-to-XTI/TLI Equivalents

Additions to the XTI Interface

9.  Packet Filtering Hooks

10.  Transport Selection and Name-to-Address Mapping

11.  Real-time Programming and Administration

12.  The Oracle Solaris ABI and ABI Tools

A.  UNIX Domain Sockets

Index

Advanced XTI/TLI Topics

This section presents additional XTI/TLI concepts:

Asynchronous Execution Mode

Many XTI/TLI library routines block to wait for an incoming event. However, some time-critical applications should not block for any reason. An application can do local processing while waiting for some asynchronous XTI/TLI event.

Applications can access asynchronous processing of XTI/TLI events through the combination of asynchronous features and the non-blocking mode of XTI/TLI library routines. See the ONC+ Developer’s Guide for information on use of the poll(2) system call and the I_SETSIG ioctl(2) command to process events asynchronously.

You can run each XTI/TLI routine that blocks for an event in a special non-blocking mode. For example, t_listen(3NSL) normally blocks for a connect request. A server can periodically poll a transport endpoint for queued connect requests by calling t_listen(3NSL) in the non-blocking (or asynchronous) mode. You enable the asynchronous mode by setting O_NDELAY or O_NONBLOCK in the file descriptor. Set these modes as a flag through t_open(3NSL), or by calling fcntl(2) before calling the XTI/TLI routine. Use fcntl(2) to enable or disable this mode at any time. All program examples in this chapter use the default synchronous processing mode.

Use of O_NDELAY or O_NONBLOCK affects each XTI/TLI routine differently. You need to determine the exact semantics of O_NDELAY or O_NONBLOCK for a particular routine.

Advanced XTI/TLI Programming Example

Example 8-2 demonstrates two important concepts. The first is a server's ability to manage multiple outstanding connect requests. The second is event-driven use of XTI/TLI and the system call interface.

By using XTI/TLI, a server can manage multiple outstanding connect requests. One reason to receive several simultaneous connect requests is to prioritize the clients. A server can receive several connect requests, and accept them in an order based on the priority of each client.

The second reason for handling several outstanding connect requests is to overcome the limits of single-threaded processing. Depending on the transport provider, while a server is processing one connect request, other clients see the server as busy. If multiple connect requests are processed simultaneously, the server is busy only if more than the maximum number of clients try to call the server simultaneously.

The server example is event-driven: the process polls a transport endpoint for incoming XTI/TLI events and takes the appropriate actions for the event received. The example following demonstrates the ability to poll multiple transport endpoints for incoming events.

Example 8-2 Endpoint Establishment (Convertible to Multiple Connections)

#include <tiuser.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <stropts.h>
#include <signal.h>

#define NUM_FDS 1
#define MAX_CONN_IND 4
#define SRV_ADDR 1                 /* server's well known address */

int conn_fd;                       /* server connection here */
extern int t_errno;
/* holds connect requests */
struct t_call *calls[NUM_FDS][MAX_CONN_IND];

main()
{
   struct pollfd pollfds[NUM_FDS];
   struct t_bind *bind;
   int i;

   /*
    * Only opening and binding one transport endpoint, but more can
    * be supported
    */
   if ((pollfds[0].fd = t_open(“/dev/tivc”, O_RDWR,
         (struct t_info *) NULL)) == -1) {
      t_error(“t_open failed”);
      exit(1);
   }
   if ((bind = (struct t_bind *) t_alloc(pollfds[0].fd, T_BIND,
         T_ALL)) == (struct t_bind *) NULL) {
      t_error(“t_alloc of t_bind structure failed”);
      exit(2);
   }
   bind->qlen = MAX_CONN_IND;
   bind->addr.len = sizeof(int);
   *(int *) bind->addr.buf = SRV_ADDR;
   if (t_bind(pollfds[0].fd, bind, bind) == -1) {
      t_error(“t_bind failed”);
      exit(3);
   }
   /* Was the correct address bound? */
   if (bind->addr.len != sizeof(int) ||
      *(int *)bind->addr.buf != SRV_ADDR) {
      fprintf(stderr, “t_bind bound wrong address\n”);
      exit(4);
   }
}

The file descriptor returned by t_open(3NSL) is stored in a pollfd structure that controls polling of the transport endpoints for incoming data. See the poll(2) man page. Only one transport endpoint is established in this example. However, the remainder of the example is written to manage multiple transport endpoints. Several endpoints could be supported with minor changes to Example 8-2.

This server sets qlen to a value greater than 1 for t_bind(3NSL). This value specifies that the server should queue multiple outstanding connect requests. The server accepts the current connect request before accepting additional connect requests. This example can queue up to MAX_CONN_IND connect requests. The transport provider can negotiate the value of qlen to be smaller if the provider cannot support MAX_CONN_IND outstanding connect requests.

After the server binds its address and is ready to process connect requests, it behaves as shown in the following example.

Example 8-3 Processing Connection Requests

pollfds[0].events = POLLIN;

while (TRUE) {
    if (poll(pollfds, NUM_FDS, -1) == -1) {
        perror(“poll failed”);
        exit(5);
    }
    for (i = 0; i < NUM_FDS; i++) {
        switch (pollfds[i].revents) {
            default:
                perror(“poll returned error event”);
                exit(6);
            case 0:
                continue;
            case POLLIN:
                do_event(i, pollfds[i].fd);
                service_conn_ind(i, pollfds[i].fd);
        }
    }
}

The events field of the pollfd structure is set to POLLIN, which notifies the server of any incoming XTI/TLI events. The server then enters an infinite loop in which it polls the transport endpoints for events, and processes events as they occur.

The poll(2) call blocks indefinitely for an incoming event. On return, the server checks the value of revents for each entry, one per transport endpoint, for new events. If revents is 0, the endpoint has generated no events and the server continues to the next endpoint. If revents is POLLIN, there is an event on the endpoint. The server calls do_event to process the event. Any other value in revents indicates an error on the endpoint, and the server exits. With multiple endpoints, the server should close this descriptor and continue.

Each time the server iterates the loop, it calls service_conn_ind to process any outstanding connect requests. If another connect request is pending, service_conn_ind saves the new connect request and responds to it later.

The server calls do_event in the following example to process an incoming event.

Example 8-4 Event Processing Routine

do_event( slot, fd)
int slot;
int fd;
{
   struct t_discon *discon;
   int i;

   switch (t_look(fd)) {
   default:
      fprintf(stderr, "t_look: unexpected event\n");
      exit(7);
   case T_ERROR:
      fprintf(stderr, "t_look returned T_ERROR event\n");
      exit(8);
   case -1:
      t_error("t_look failed");
      exit(9);
   case 0:
      /* since POLLIN returned, this should not happen */
      fprintf(stderr,"t_look returned no event\n");
      exit(10);
   case T_LISTEN:
      /* find free element in calls array */
      for (i = 0; i < MAX_CONN_IND; i++) {
         if (calls[slot][i] == (struct t_call *) NULL)
            break;
      }
      if ((calls[slot][i] = (struct t_call *) t_alloc( fd, T_CALL,
               T_ALL)) == (struct t_call *) NULL) {
         t_error("t_alloc of t_call structure failed");
         exit(11);
      }
      if (t_listen(fd, calls[slot][i] ) == -1) {
         t_error("t_listen failed");
         exit(12);
      }
      break;
   case T_DISCONNECT:
      discon = (struct t_discon *) t_alloc(fd, T_DIS, T_ALL);
      if (discon == (struct t_discon *) NULL) {
         t_error("t_alloc of t_discon structure failed");
         exit(13)
      }
      if(t_rcvdis( fd, discon) == -1) {
         t_error("t_rcvdis failed");
         exit(14);
      }
      /* find call ind in array and delete it */
      for (i = 0; i < MAX_CONN_IND; i++) {
         if (discon->sequence == calls[slot][i]->sequence) {
            t_free(calls[slot][i], T_CALL);
            calls[slot][i] = (struct t_call *) NULL;
         }
      }
      t_free(discon, T_DIS);
      break;
   }
}

The arguments in Example 8-4 are a number (slot) and a file descriptor (fd). A slot is the index into the global array calls, which has an entry for each transport endpoint. Each entry is an array of t_call structures that hold incoming connect requests for the endpoint.

The do_event module calls t_look(3NSL) to identify the XTI/TLI event on the endpoint specified by fd. If the event is a connect request (T_LISTEN event) or disconnect request (T_DISCONNECT event), the event is processed. Otherwise, the server prints an error message and exits.

For connect requests, do_event scans the array of outstanding connect requests for the first free entry. A t_call structure is allocated for the entry, and the connect request is received by t_listen(3NSL). The array is large enough to hold the maximum number of outstanding connect requests. The processing of the connect request is deferred.

A disconnect request must correspond to an earlier connect request. The do_event module allocates a t_discon structure to receive the request. This structure has the following fields:

struct t_discon {
    struct    netbuf    udata;
    int       reason;
    int       sequence;
}

The udata structure contains any user data sent with the disconnect request. The value of reason contains a protocol-specific disconnect reason code. The value of sequence identifies the connect request that matches the disconnect request.

The server calls t_rcvdis(3NSL) to receive the disconnect request. The array of connect requests is scanned for one that contains the sequence number that matches the sequence number in the disconnect request. When the connect request is found, its structure is freed and the entry is set to NULL.

When an event is found on a transport endpoint, service_conn_ind is called to process all queued connect requests on the endpoint, as the following example shows.

Example 8-5 Process All Connect Requests

service_conn_ind(slot, fd)
{
   int i;

    for (i = 0; i < MAX_CONN_IND; i++) {
      if (calls[slot][i] == (struct t_call *) NULL)
         continue;
      if((conn_fd = t_open( “/dev/tivc”, O_RDWR,
            (struct t_info *) NULL)) == -1) {
         t_error("open failed");
         exit(15);
      }
      if (t_bind(conn_fd, (struct t_bind *) NULL,
            (struct t_bind *) NULL) == -1) {
         t_error("t_bind failed");
         exit(16);
      }
      if (t_accept(fd, conn_fd, calls[slot][i]) == -1) {
         if (t_errno == TLOOK) {
            t_close(conn_fd);
            return;
         }
         t_error("t_accept failed");
         exit(167);
      }
      t_free(calls[slot][i], T_CALL);
      calls[slot][i] = (struct t_call *) NULL;
      run_server(fd);
   }
}

For each transport endpoint, the array of outstanding connect requests is scanned. For each request, the server opens a responding transport endpoint, binds an address to the endpoint, and accepts the connection on the endpoint. If another connect or disconnect request arrives before the current request is accepted, t_accept(3NSL) fails and sets t_errno to TLOOK. You cannot accept an outstanding connect request if any pending connect request events or disconnect request events exist on the transport endpoint.

If this error occurs, the responding transport endpoint is closed and service_conn_ind returns immediately, saving the current connect request for later processing. This activity causes the server's main processing loop to be entered, and the new event is discovered by the next call to poll(2). In this way, the user can queue multiple connect requests.

Eventually, all events are processed, and service_conn_ind is able to accept each connect request in turn.