Previous section.

Networking Services (XNS) Issue 5
Copyright © 1997 The Open Group

Guidelines for Use of XTI

Transport Service Interface Sequence of Functions

In order to describe the allowable sequence of function calls, this section gives some rules regarding the maintenance of the state of the interface:

The following rules apply only to the connection-mode transport service:

Example in Connection-oriented Mode

Sequence of Transport Functions in Connection-oriented Mode shows the allowable sequence of functions of an active user and passive user communicating using a connection-mode transport service. This example is not meant to show all the functions that must be called, but rather to highlight the important functions that request a particular service. Blank lines are used to indicate that the function would be called by another user prior to a related function being called by the remote user. For example, the active user calls t_connect() to request a connection and the passive user would receive an indication of the connection request (via the return from t_listen()) and then would call the t_accept().

The state diagram in Sequence of Transport Functions in Connection-oriented Mode shows the flow of the events through the various states. The active user is represented by a solid line and the passive user is represented by a dashed line. This example shows a successful connection being established and terminated using connection-mode transport service without orderly release. For a detailed description of all possible states and events, see Connection/Release/Data Transfer States: Connection-mode .


Active User   Passive User
t_open()   t_open()
t_bind()   t_bind()
    t_listen()
t_connect()    
    t_accept()
t_rcvconnect()    
t_snd()    
t_sndv()    
    t_rcv()
    t_rcvv()
t_snddis()    
    t_rcvdis()
t_unbind()   t_unbind()
t_close()   t_close()


Table: Sequence of Transport Functions in Connection-oriented Mode

Example in Connectionless Mode

Sequence of Transport Functions in Connectionless Mode shows the allowable sequence of functions of user A and user B communicating using a connectionless-mode transport service. This example is not meant to show all the functions that must be called but rather to highlight the important functions that request a particular service. Blank lines are used to indicate that a function would be called by another user prior to a related function being called by the remote user.

The state diagram that follows shows the flow of the events through the various states. This example shows a successful exchange of data between user A and user B. For a detailed description of all possible states and events, see Connection/Release/Data Transfer States: Connection-mode .


User A   User B
t_open()   t_open()
t_bind()   t_bind()
t_sndudata()    
    t_rcvudata()
t_unbind()   t_unbind()
t_close()   t_close()


Table: Sequence of Transport Functions in Connectionless Mode

Writing Protocol-independent Software

In order to maximise portability of XTI applications between different kinds of machines and to support protocol independence, there are some general rules:

  1. An application should only make use of those functions and mechanisms described as being mandatory features of XTI.

  2. In the connection-mode service, the concept of a transport service data unit (TSDU) may not be supported by all transport providers. The user should make no assumptions about the preservation of logical data boundaries across a connection.

  3. If an application is not intended to run only over an ISO transport provider, then the name of the device should not be hard-coded into it. While software may be written for a particular class of service (for example, connectionless-mode service), it should not be written to depend on any attribute of the underlying protocol.

  4. The protocol-specific service limits returned on the t_open() and t_getinfo() functions must not be exceeded. It is the responsibility of the user to access these limits and then adhere to the limits throughout the communication process.

  5. The user program should not look at or change options that are specific to the underlying protocol. The t_optmgmt() function enables a user to access default protocol options from the transport provider, which may then be blindly passed as an argument on the appropriate connection establishment function. Optionally, the user can choose not to pass options as an argument on connection establishment functions.

  6. Protocol-specific addressing issues should be hidden from the user program. Similarly, the user must have some way of accessing destination addresses in an invisible manner, such as through a name server. However, the details for doing so are outside the scope of this interface specification.

  7. The reason codes associated with t_rcvdis() are protocol-dependent. The user should not interpret this information if protocol independence is a concern.

  8. The error codes associated with t_rcvuderr() are protocol-dependent. The user should not interpret this information if protocol independence is a concern.

  9. The orderly release facility of the connection-mode service (that is, t_sndrel() and t_rcvrel()) should not be used by programs targeted for multiple protocol environments. This facility is not supported by all connection-based transport protocols. In particular, its use will prevent programs from successfully communicating with ISO open systems.

  10. The semantics of expedited data are different across different transport providers (for example, ISO and TCP). An application intended to run over different transport providers should avoid their use.

  11. The semantics of closing a connection may be different across transport providers. For example, closing a connection to ISO is abortive while closing a connection to TCP is orderly. A portable application should not assume either facility is available. If the service provider is of type COTS_ORD, a portable application should use t_sndrel() / t_rcvrel() prior to calling t_close().

Event Management

In the absence of a standardised Event Management interface, the following guidelines are offered for the use of existing and widely available mechanisms by XTI applications.

These guidelines provide information additional to that given in Synchronous and Asynchronous Execution Modes and Event Management .

For applications to use XTI in a fully asynchronous manner, they will need to use the facilities of an Event Management (EM) Interface. Such an EM will allow the application to be notified of a number of XTI events over a range of active endpoints. These events may be associated with:

In the same way, the EM mechanism should allow the application to be notified of events coming from external sources, such as:

When handling multiple transport connections, the application could either:

The application will have to maintain an appropriate balance and choose the right trade-off between the number of processes and the number of connections managed per process in order to minimise the resulting overhead.

Unfortunately, the system facilities to suspend and await notification of an event are presently system-dependent, although work is in progress within standards bodies to provide a unified and portable mechanism.

Hence, for the foreseeable future, applications could use whatever underlying system facilities exist for event notification.

Short-term Solution

Many vendors currently provide either the System V poll() or BSD select() system calls which both give the ability to suspend until there is activity on a member of a set of file descriptors or a timeout.

Given the fact that a transport endpoint identifying a transport connection maps to a file descriptor, applications can take advantage of such EM mechanisms offered by the system (for example, poll() or select()). The design of more efficient and sophisticated applications, that make full use of all the XTI features, then becomes easily possible.

Guidelines for the use of poll() and select() are included in manual-page format, following the end of this section.

XTI Events

The XTI events can be divided into two classes of events.

The Poll Function

Refer to the description of poll() in the referenced XSH specification. Moreover, Sockets Interfaces of the current document gives additional information on the specific effect of poll() when applied to Sockets.

Example of Use of Poll

The following description gives the outline of an XTI server program making use of the poll() function.

/*
 * This is a simple server application example to show how poll() can
 * be used in a portable manner to wait for the occurrence of XTI events.
 * In this example, poll() is used to wait for the events T_LISTEN,
 * T_DISCONNECT, T_DATA and T_GODATA.
 *
 * A transport endpoint is opened in asynchronous mode over a
 * message-oriented transport provider (for example, ISO). The endpoint
 * is bound with qlen = 1 and the application enters an endless loop
 * to wait for all incoming XTI events on all its active endpoints.
 * For all connection indications received, a new endpoint is opened
 * with qlen = 0 and the connection request is accepted on that endpoint.
 * For all established connections, the application waits for data
 * to be received from one of its clients, sends the received data
 * back to the sender and waits for data again.
 * The cycle repeats until all the connections are released by
 * the clients. The disconnection indications are processed and the
 * endpoints closed.
 *
 * The example references two fictitious functions:
 *
 * -  int get_provider(int tpid, char * tpname)
 *      Given a number as transport provider id, the function returns in
 *      tpname a string as transport provider name that can be used with
 *      t_open(). This function hides the different naming schemes of
 *      different XTI implementations.
 *
 * -  int get_address(char * symb_name, struct netbuf address)
 *      Given a symbolic name symb_name and a pointer to a struct netbuf
 *      with allocated buffer space as input, the function returns a
 *      protocol address. This function hides the different addressing
 *      schemes of different XTI implementations.
 */

/*
 * General Includes
 */
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <xti.h>

/*
 * Include files for poll()
 */
#include <poll.h>



/*
 * Various Defines
 */
/*
 * The XTI events T_CONNECT, T_DISCONNECT, T_LISTEN, T_ORDREL and T_UDERR
 * are related to one of the poll flags in INEVENTS (to which one, depends
 * on the implementation). POLLOUT means that (at least) normal data may
 * be sent, and POLLWRBAND that expedited data may be sent.
 */

#define ERREVENTS (POLLERR | POLLHUP | POLLNVAL)
#define INEVENTS (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
#define OUTEVENTS (POLLOUT | POLLWRBAND)
#define MY_PROVIDER 1 /* transport provider id */
#define MAXSIZE 4000 /* size of send/receive buffer */
#define TPLEN 30 /* maximum length of provider name */
#define MAXCNX 10 /* maximum number of connections */


extern int      errno;

/*
 *  Declaration of non-integer external functions
 */
void    exit();
void    perror();

/* ============================================================== */



main()
{

  register int i; /* loop variable */
  register int num; /* return value of t_snd() */
      /* and t_rcv() */
  int discflag = 0; /* flag to indicate a */
      /* disc indication */
  int errflag = 0; /* flag to indicate an error */
  int event; /* stores events returned */
      /* by t_look() */
  int fd; /* current file descriptor */
  int fdd; /* file descriptor */
      /* for t_accept() */
  int flags; /* used with t_rcv() */
  char *datbuf; /* current send/receive buffer */
  unsigned int act = 0; /* active endpoints */
  struct t_info info; /* used with t_open() */
  struct t_bind *preq; /* used with t_bind() */
  struct t_call *pcall; /* used with t_listen() */
      /* and t_accept() */
  struct t_discon discon; /* used with t_rcvdis() */
  char tpname[TPLEN]; /* transport provider name */
  char buf[MAXCNX][MAXSIZE]; /* send/receive buffers */
  int rcvdata[MAXCNX]; /* amount of data */
      /* already received */
  int snddata[MAXCNX]; /* amount of data already sent */
           
  struct pollfd fds[MAXCNX]; /* used with poll() */


    /*
     * Get name of transport provider
     */
    if (get_provider(MY_PROVIDER, tpname) == -1) {
            perror(">>> get_provider failed");
            exit(1);
    }

    /*
     * Establish a transport endpoint in asynchronous mode
     */
    if ((fd = t_open(tpname, O_RDWR | O_NONBLOCK, &info)) == -1) {
            t_error(">>> t_open failed");
            exit(1);
    }



    /*
     * Allocate memory for the parameters passed with t_bind().
     */
    if ((preq = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR)) == NULL) {
            t_error(">>> t_alloc(T_BIND) failed");
            t_close(fd);
            exit(1);
    }

    /*
     * Given a symbolic name ("MY_NAME"), get_address returns an address
     * and its length in preq->addr.buf and preq->addr.len.
     */
    if (get_address("MY_NAME", &(preq->addr)) == -1) {
            perror(">>> get_address failed");
            t_close(fd);
            exit(1);
    }
    preq->qlen = 1;         /* is a listening endpoint */

    /*
     *  Bind the local protocol address to the transport endpoint.
     *  The returned information is discarded.
     */
    if (t_bind(fd, preq, NULL) == -1) {
            t_error(">>> t_bind failed");
            t_close(fd);
            exit(1);
    }
    if (t_free(preq, T_BIND) == -1) {
            t_error(">>> t_free failed");
            t_close(fd);
            exit(1);
    }

    /*
     * Allocate memory for the parameters used with t_listen.
     */
    if ((pcall = (struct t_call *) t_alloc(fd, T_CALL, T_ALL)) == NULL) {
            t_error(">>> t_alloc(T_CALL) failed");
            t_close(fd);
            exit(1);
    }



    /*
     * Initialise entry 0 of the fds array to the listening endpoint.
     * To be portable across different XTI implementations,
     * register for INEVENTS and not for POLLIN.
     */
    fds[act].fd = fd;
    fds[act].events = INEVENTS;
    fds[act].revents = 0;
    rcvdata[act] = 0;
    snddata[act] = 0;
    act = 1;

    /*
     * Enter an endless loop to wait for all incoming events.
     * Connect requests are accepted on new opened endpoints.
     * The example assumes that data is first sent by the client.
     * Then, the received data is sent back again and so on, until
     * the client disconnects.
     * Note that the total number of active endpoints (act) should
     * at least be 1, corresponding to the listening endpoint.
     */
    fprintf(stderr, "Waiting for XTI events...\n");
    while (act > 0) {
        /*
         * Wait for any events
         *
         */
        if (poll(&fds, (size_t)act, (int) -1) == -1) {
        perror(">>> poll failed");
                    exit(1);
        }
        /*
         * Process incoming events on all active endpoints
         */
        for (i = 0 ; i < act ; i++) {
            if (fds[i].revents == 0)
                continue;       /* no event for this endpoint */
            if (fds[i].revents & ERREVENTS) {
                fprintf(stderr, "[%d] Unexpected poll events: 0x%x\n",
                                fds[i].fd, fds[i].revents);
                continue;
            }
            /*
             * set the current endpoint
             * set the current send/receive buffer
             */
            fd = fds[i].fd;
            datbuf = buf[i];



            /*
             * Check for events
             */
            switch((event = t_look(fd))) {
            case T_LISTEN:
                /*
                 * Must be a connection indication
                 */
                if (t_listen(fd, pcall) == -1) {
                    t_error(">>> t_listen failed");
                    exit(1);
                }
                /*
                 * If it will exceed the maximum number
                 * of connections that the server can handle,
                 * reject the connection indication.
                 */
                if (act >= MAXCNX) {
                    fprintf(stderr, ">>> Connection request rejected\n");
                    if (t_snddis(fd, pcall) == -1)
                        t_error(">>> t_snddis failed");
                    continue;
                }
                /*
                 * Establish a transport endpoint
                 * in asynchronous mode
                 */
                if ((fdd = t_open(tpname, O_RDWR | O_NONBLOCK, &info))
                                                                 == -1) {
                    t_error(">>> t_open failed");
                    continue;
                }
                /*
                 * Accept connection on this endpoint.
                 * fdd no longer needs to be bound,
                 * t_accept() will do it.
                 */
                if (t_accept(fd, fdd, pcall) == -1) {
                    t_error(">>> t_accept failed");
                    t_close(fdd);
                    continue;
                }
                fprintf(stderr, "Connection [%d] opened\n", fdd);



                /*
                 * Register for all flags that might indicate
                 * a T_DATA or T_DISCONNECT event, i. e.,
                 * register for INEVENTS (to be portable
                 * through all XTI implementations).
                 */
                fds[act].fd = fdd;
                fds[act].events = INEVENTS;
                fds[act].revents = 0;
                rcvdata[act] = 0;
                snddata[act] = 0;
                act++;
                break;

            case T_DATA:
                /*
                 * Must be a data indication
                 */
                if ((num = t_rcv(fd, (datbuf + rcvdata[i]),
                   (MAXSIZE - rcvdata[i]), &flags)) == -1) {
                    switch (t_errno) {
                    case TNODATA:
                         /* No data is currently
                         * available: repeat the loop
                         */
                        continue;
                    case TLOOK:
                         /* Must be a T_DISCONNECT event:
                         * set discflag
                         */
                        event = t_look(fd);
                        if (event == T_DISCONNECT) {
                            discflag = 1;
                            break;
                        }
                        else
                            fprintf(stderr, "Unexpected event %d\n",
                                                                event);
                    default:
                        /* Unexpected failure */
                        t_error(">>> t_rcv failed");
                        fprintf(stderr, "connection id: [%d]\n", fd);
                        errflag = 1;
                        break;
                    }
                }



                if (discflag || errflag)
                                    /* exit from the event switch */
                    break;
                fprintf(stderr, "[%d] %d bytes received\n", fd, num);
                rcvdata[i] += num;
                if (rcvdata[i] < MAXSIZE)
                    continue;
                if (flags & T_MORE) {
                    fprintf(stderr, "[%d] TSDU too long for receive
                                                    buffer\n", fd);
                    errflag = 1;
                    break;  /* exit from the event switch */
                }

                /*
                 * Send the data back:
                 * Repeat t_snd() until either the whole TSDU
                 * is sent back, or an event occurs.
                 */
                fprintf(stderr, "[%d] sending data back\n", fd);
                do {
                    if ((num = t_snd(fd, (datbuf + snddata[i]),
                        (MAXSIZE - snddata[i]), 0)) == -1) {
                        switch (t_errno) {
                        case TFLOW:
                            /*
                             * Register for the flags
                             * OUTEVENTS to get awaken by
                             * T_GODATA, and for INEVENTS
                             * to get aware of T_DISCONNECT
                             * or T_DATA.
                             */
                            fds[i].events |= OUTEVENTS;
                            continue;



                        case TLOOK:
                            /*
                             * Must be a T_DISCONNECT event:
                             * set discflag
                             */
                            event = t_look(fd);
                            if (event == T_DISCONNECT) {
                                discflag = 1;
                                break;
                            }
                            else
                                fprintf(stderr, "Unexpected event %d\n",
                                                                  event);

                        default:
                            t_error(">>> t_snd failed");
                            fprintf(stderr, "connection id: [%d]\n", fd);
                            errflag = 1;
                            break;
                        }
                     }
                     else {
                        snddata[i] += num;
                     }
                } while (MAXSIZE > snddata[i] && !discflag && !errflag);
                /*
                 * Reset send/receive counters
                 */
                rcvdata[i] = 0;
                snddata[i] = 0;
                break;



            case T_GODATA:
                /*
                 * Flow control restriction has been lifted
                 * restore initial event flags
                 */
                fds[i].events = INEVENTS;
                continue;
            case T_DISCONNECT:
                /*
                 * Must be a disconnection indication
                 */
                discflag = 1;
                break;
            case -1:
                /*
                 * Must be an error
                 */
                t_error(">>> t_look failed");
                errflag = 1;
                break;
            default:
                /*
                 * Must be an unexpected event
                 */
                fprintf(stderr, "[%d] Unexpected event %d\n", fd, event);
                errflag = 1;
                break;
            }       /* end event switch */

            if (discflag) {
                /*
                 * T_DISCONNECT has been received.
                 * User data is not expected.
                 */
                if (t_rcvdis(fd, &discon) == -1)
                    t_error(">>> t_rcvdis failed");
                else
                    fprintf(stderr, "[%d] Disconnection reason: 0x%x\n",
                                                      fd, discon.reason);
            }



            if (discflag || errflag) {
                /*
                 * Close transport endpoint and
                 * decrement number of active connections
                 */
                t_close(fd);
                act--;
                /* Move last entry of fds array to current slot,
                 * adjust internal counters and flags
                 */
                fds[i].events = fds[act].events;
                fds[i].revents = fds[act].revents;
                fds[i].fd = fds[act].fd;
                discflag = 0;   /* clear disconnection flag */
                errflag = 0;    /* clear error flag */
                i--;    /* Redo the for() event loop to consider
                         * events related to the last entry of
                         * fds array */
                fprintf(stderr, "Connection [%d] closed\n", fd);
            }

        }               /* end of for() event loop */

    }       /* end of while() loop */
    fprintf(stderr, ">>> Warning: no more active endpoints\n");
    exit(1);
}


The Select Function

Refer to the description of select() in the referenced XSH specification. Moreover, Sockets Interfaces of the current document gives additional information on the specific effect of select() when applied to Sockets.

Example of Use of Select

The following gives the outline of an XTI server program making use of select(). The following describes the outline of an XTI server program making use of the select() function.


/*
 * This is a simple server application example to show how select() can
 * be used in a portable manner to wait for the occurrence of XTI events.
 * In this example, select() is used to wait for the events T_LISTEN,
 * T_DISCONNECT, T_DATA and T_GODATA.
 *
 * A transport endpoint is opened in asynchronous mode over a
 * message-oriented transport provider (for example, ISO). The endpoint is
 * bound with qlen = 1, and the application enters an endless loop to wait
 * for all incoming XTI events on all its active endpoints.
 * For all connection indications received, a new endpoint is opened with
 * qlen = 0 and the connection request is accepted on that endpoint.
 * For all established connections, the application waits for data to be
 * received from one of its clients, sends the received data back to the
 * sender and waits for data again.
 * The cycle repeats until all the connections are released by the clients.
 * The disconnection indications are processed and the endpoints closed.
 *
 * The example references two fictitious functions:
 *
 * -  int get_provider(int tpid, char * tpname)
 *      Given a number as transport provider id, the function returns in
 *      tpname a string as transport provider name that can be used with
 *      t_open(). This function hides the different naming schemes of
 *      different XTI implementations.
 *
 * -  int get_address(char * symb_name, struct netbuf address)
 *      Given a symbolic name symb_name and a pointer to a struct netbuf
 *      with allocated buffer space as input, the function returns a
 *      protocol address. This function hides the different addressing
 *      schemes of different XTI implementations.
 */
/*
 * General Includes
 */
#include <fcntl.h>
#include <stdio.h>
#include <xti.h>
/*
 * Include files for select(). 
 */
#include <sys/select.h>
#include <sys/time.h>



/*
 * Various Defines
 */

#define MY_PROVIDER 1 /* transport provider id */
#define MAXSIZE 4000 /* size of send/receive buffer */
#define TPLEN 30 /* maximum length of provider name */
#define MAXCNX 10 /* maximum number of connections */


extern int      errno;



/*
 *  Declaration of non-integer external functions.
 */
void    exit();
void    perror();

/* ============================================================== */

main()
{

  register int i; /* loop variable */
  register int num; /* return value of t_snd() */
      /* and t_rcv() */
           
  int discflag = 0; /* flag to indicate a */
      /* disc indication */
  int errflag = 0; /* flag to indicate an error */
  int event; /* stores events returned */
      /* by t_look() */
  int fd; /* current file descriptor */
  int fdd; /* file descriptor */
      /* for t_accept() */
  int flags; /* used with t_rcv() */
  char *datbuf; /* current send/receive */
      /* buffer */
  size_t act = 0; /* active endpoints */
  struct t_info info; /* used with t_open() */
  struct t_bind *preq; /* used with t_bind() */
  struct t_call *pcall; /* used with t_listen() */
      /* and t_accept() */
  struct t_discon discon; /* used with t_rcvdis() */
  char tpname[TPLEN]; /* transport provider name */
           
  int fds[MAXCNX]; /* array of file descriptors */
  char buf[MAXCNX][MAXSIZE] /* send/receive buffers */
  int rcvdata[MAXCNX]; /* amount of data */
      /* already received */
  int snddata[MAXCNX]; /* amount of data already sent */
           
  fd_set rfds, wfds, xfds; /* file descriptor sets */
    /* for select() */
  fd_set rfdds, wfdds, xfdds; /* initial values of */
    /* file descriptor sets */
    /* rfds, wfds and xfds */




    /*
     * Get name of transport provider
     */
    if (get_provider(MY_PROVIDER, tpname) == -1) {
        perror(">>> get_provider failed");
        exit(1);
    }

    /*
     * Establish a transport endpoint in asynchronous mode
     */
    if ((fd = t_open(tpname, O_RDWR | O_NONBLOCK, &info)) == -1) {
        t_error(">>> t_open failed");
        exit(1);
    }

    /*
     * Allocate memory for the parameters passed with t_bind().
     */
    if ((preq = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR)) == NULL) {
        t_error(">>> t_alloc(T_BIND) failed");
        t_close(fd);
        exit(1);
    }

    /*
     * Given a symbolic name ("MY_NAME"), get_address returns an address
     * and its length in preq->addr.buf and preq->addr.len.
     */
    if (get_address("MY_NAME", &(preq->addr)) == -1) {
        perror(">>> get_address failed");
        t_close(fd);
        exit(1);
    }
    preq->qlen = 1;         /* is a listening endpoint */



    /*
     *  Bind the local protocol address to the transport endpoint.
     *  The returned information is discarded.
     */
    if (t_bind(fd, preq, NULL) == -1) {
        t_error(">>> t_bind failed");
        t_close(fd);
        exit(1);
    }
    if (t_free(preq, T_BIND) == -1) {
        t_error(">>> t_free failed");
        t_close(fd);
        exit(1);
    }

    /*
     * Allocate memory for the parameters used with t_listen.
     */
    if ((pcall = (struct t_call *) t_alloc(fd, T_CALL, T_ALL)) == NULL) {
        t_error(">>> t_alloc(T_CALL) failed");
        t_close(fd);
        exit(1);
    }

    /*
     * Initialise listening endpoint in descriptor set.
     * To be portable across different XTI implementations,
     * register for descriptor set rfdds and xfdds
     */
    FD_ZERO(&rfdds);
    FD_ZERO(&xfdds);
    FD_ZERO(&wfdds);
    FD_SET(fd, &rfdds);
    FD_SET(fd, &xfdds);
    fds[act] = fd;
    rcvdata[act] = 0;
    snddata[act] = 0;
    act = 1;



    /*
     * Enter an endless loop to wait for all incoming events.
     * Connect requests are accepted on a new opened endpoint.
     * The example assumes that data is first sent by the client.
     * Then, the received data is sent back again and so on, until
     * the client disconnects.
     * Note that the total number of active endpoints (act) should
     * at least be 1, corresponding to the listening endpoint.
     */
    fprintf(stderr, "Waiting for XTI events...\n");
    while (act > 0) {
        /*
         * Wait for any events
         */

    /*
     * Set the mask sets rfds, xfds and wfds to their initial values
     */
    rfds = rfdds;
    xfds = xfdds;
    wfds = wfdds;
    if (select(OPEN_MAX, &rfds, &wfds, &xfds,
            (struct timeval *) NULL) == -1) {
        perror(">>> select failed");
        exit(1);
    }
    /*
     * Process incoming events on all active endpoints
     */
    for (i = 0 ; i < act ; i++) {
        /*
         * set the current endpoint
         * set the current send/receive buffer
         */
        fd = fds[i];
        datbuf = buf[i];

        if (FD_ISSET(fd, &xfds)) {
            fprintf(stderr, "[%d] Unexpected select events\n", fd);
            continue;
        }
        if (!FD_ISSET(fd, &rfds) && !FD_ISSET(fd, &wfds))
            continue;       /* no event for this endpoint */



        /*
         * Check for events
         */
        switch((event = t_look(fd))) {
        case T_LISTEN:
            /*
             * Must be a connection indication
             */
            if (t_listen(fd, pcall) == -1) {
                t_error(">>> t_listen failed");
                exit(1);
            }

            /*
             * If it will exceed the maximum number
             * of connections that the server can handle,
             * reject the connection indication.
             */
            if (act >= MAXCNX) {
                fprintf(stderr, ">>> Connection request
                                                rejected\n");
                if (t_snddis(fd, pcall) == -1)
                    t_error(">>> t_snddis failed");
                continue;
            }
            /*
             * Establish a transport endpoint
             * in asynchronous mode
             */
            if ((fdd = t_open(tpname, O_RDWR | O_NONBLOCK,
                                              &info)) == -1) {
                t_error(">>> t_open failed");
                continue;
            }
            /*
             * Accept connection on this endpoint.
             * fdd no longer needs to be bound,
             * t_accept() will do it
             */
            if (t_accept(fd, fdd, pcall) == -1) {
                t_error(">>> t_accept failed");
                t_close(fdd);
                continue;
            }
            fprintf(stderr, "Connection [%d] opened\n", fdd);



            /*
             * Register for all flags that might indicate
             * a T_DATA or T_DISCONNECT event, i. e.,
             * register for rfdds and xfdds (to be portable
             * through all XTI implementations).
             */
            fds[act] = fdd;
            FD_SET(fdd, &rfdds);
            FD_SET(fdd, &xfdds);
            rcvdata[act] = 0;
            snddata[act] = 0;
            act++;
            break;

        case T_DATA:
             /* Must be a data indication
             */
            if ((num = t_rcv(fd, (datbuf + rcvdata[i]),
               (MAXSIZE - rcvdata[i]), &flags)) == -1) {
                switch (t_errno) {
                case TNODATA:
                     /* No data is currently
                     * available: repeat the loop
                     */
                    continue;
                case TLOOK:
                     /* Must be a T_DISCONNECT event:
                     * set discflag
                     */
                    event = t_look(fd);
                    if (event == T_DISCONNECT) {
                        discflag = 1;
                        break;
                    }
                    else
                        fprintf(stderr, "Unexpected event %d\n", event);

                default:
                    /* Unexpected failure */
                    t_error(">>> t_rcv failed");
                    fprintf(stderr, "connection id: [%d]\n", fd);
                    errflag = 1;
                    break;
                }
            }



            if (discflag || errflag)
                /* exit from the event switch */
                break;
            fprintf(stderr, "[%d] %d bytes received\n", fd, num);
            rcvdata[i] += num;
            if (rcvdata[i] < MAXSIZE)
                continue;
            if (flags & T_MORE) {
                fprintf(stderr, "[%d] TSDU too long for receive
                                                  buffer\n", fd);
                errflag = 1;
                break;  /* exit from the event switch */
            }

            /*
             * Send the data back.
             * Repeat t_snd() until either the whole TSDU
             * is sent back, or an event occurs.
             */
            fprintf(stderr, "[%d] sending data back\n", fd);
            do {
                if ((num = t_snd(fd, (datbuf + snddata[i]),
                    (MAXSIZE - snddata[i]), 0)) == -1) {
                    switch (t_errno) {
                    case TFLOW:
                        /*
                         * Register for wfds to get
                         * awaken by T_GODATA, and for
                         * rfds and xfds to get aware of
                         * T_DISCONNECT or T_DATA.
                         */
                        FD_SET(fd, &wfdds);
                        continue;



                    case TLOOK:
                        /*
                         * Must be a T_DISCONNECT event:
                         * set discflag
                         */
                        event = t_look(fd);
                        if (event == T_DISCONNECT) {
                            discflag = 1;
                            break;
                        }
                        else
                            fprintf(stderr, "Unexpected event
                                                %d\n", event);

                    default:
                        t_error(">>> t_snd failed");
                        fprintf(stderr, "connection id: [%d]\n", fd);
                        errflag = 1;
                        break;
                    }
                }
                else {
                    snddata[i] += num;
                }
            } while (MAXSIZE > snddata[i] && !discflag && !errflag);
            /*
             * Reset send/receive counter
             */
            rcvdata[i] = 0;
            snddata[i] = 0;
            break;



        case T_GODATA:
            /*
             * Flow control restriction has been lifted
             * restore initial event flags
             */
            FD_CLR(fd, &wfdds);
            continue;
        case T_DISCONNECT:
            /*
             * Must be a disconnection indication
             */
            discflag = 1;
            break;
        case -1:
            /*
             * Must be an error
             */
            t_error(">>> t_look failed");
            errflag = 1;
            break;
        default:
            /*
             * Must be an unexpected event
             */
            fprintf(stderr, "[%d] Unexpected event %d\n", fd, event);
            errflag = 1;
            break;
        }       /* end event switch */

        if (discflag) {
            /*
             * T_DISCONNECT has been received.
             * User data is not expected.
             */
            if (t_rcvdis(fd, &discon) == -1)
                t_error(">>> t_rcvdis failed");
            else
                fprintf(stderr, "[%d] Disconnection reason: 0x%x\n",
                                               fd, discon.reason);
            }



            if (discflag || errflag) {
                /*
                 * Close transport endpoint and
                 * decrement number of active connections
                 */
                t_close(fd);
                act--;
                /*
                 * Unregister fd from initial mask sets
                 */
                FD_CLR(fd, &rfdds);
                FD_CLR(fd, &xfdds);
                FD_CLR(fd, &wfdds);
                /* Move last entry of fds array to current slot,
                 * adjust internal counters and flags
                 */
                fds[i] = fds[act];
                discflag = 0;   /* clear disconnection flag */
                errflag = 0;    /* clear error flag */
                i--;    /* Redo the for() event loop to consider
                         * events related to the last entry of
                         * fds array */
                fprintf(stderr, "Connection [%d] closed\n", fd);
            }

        }       /* end of for() event loop */

    }       /* end of while() loop */
    fprintf(stderr, ">>> Warning: no more active endpoints\n");
    exit(1);
}



Why not acquire a nicely bound hard copy?
Click here to return to the publication details or order a copy of this publication.

Contents Next section Index