Programming with the X/Open Transport Interface (XTI)

Connection establishment

The connection establishment procedures highlight the distinction between clients and servers. The Transport Interface imposes a different set of procedures in this phase for each type of transport user. When the server is ready to accept connections from any client, it calls t_listen(S), which blocks until a connection indication is received. The client initiates the connection establishment to a particular server by calling t_connect(S). The server is then notified a connection indication has arrived, and the t_listen call returns. The server may either accept or reject the client's request. It will call t_accept(S) to establish the connection, or call t_snddis(S) to reject the request. The client will be notified of the server's decision when t_connect completes. For a successful connection to be established, the server should be executing a t_listen before the client issues the t_connect.

The Transport Interface supports two facilities during connection establishment that may not be supported by all transport providers:

The client

Continuing with the client/server example, the steps needed by the client to establish a connection are shown next:

   /* By assuming that the address is an integer value,
    * this program may not run over another protocol.
   if ((sndcall = (struct t_call *)t_alloc(fd, T_CALL, T_ADDR)) == NULL) {
   	t_error("t_alloc failed");
   sndcall->addr.len = sizeof(int);
   *(int *)sndcall->addr.buf = SRV_ADDR;

if (t_connect(fd, sndcall, NULL) < 0) { t_error("t_connect failed for fd"); exit(4); }

The t_connect call establishes the connection with the server. The first argument to t_connect identifies the transport endpoint through which the connection is established, and the second argument identifies the destination server. This argument is a pointer to a t_call structure with the following members:
   struct t_call {
   	struct netbuf addr;
   	struct netbuf opt;
   	struct netbuf udata;
   	int sequence;
addr identifies the address of the server, opt may be used to specify protocol-specific options that the client would like to associate with the connection, and udata identifies user data that may be sent with the connect request to the server. The sequence member has no meaning for t_connect.

t_alloc is called to allocate the t_call structure dynamically. Once allocated, the appropriate values are assigned. In this example, no options or user data is associated with the t_connect call, but the server's address must be set. The third argument to t_alloc is set to T_ADDR to specify that an appropriate netbuf buffer should be allocated for the address. The server's address is then assigned to buf, and len is set accordingly.

The third argument to t_connect can be used to return information about the newly established connection to the user, and may retrieve any user data sent by the server in its response to the connect request. It is set to NULL by the client here to show that this information is not needed. The connection will be established on successful return of t_connect. If the server rejects the connect request, t_connect will fail and set t_errno to TLOOK.

Event handling

The TLOOK error has special significance in the Transport Interface. TLOOK notifies the user if a Transport Interface routine is interrupted by an unexpected asynchronous transport event on the given transport endpoint. As such, TLOOK does not report an error with a Transport Interface routine, but indicates that the normal processing of that routine will not be done because of the pending event. The events defined by the Transport Interface are listed here:

A request for a connection, called a connect indication, has arrived at the transport endpoint.

The confirmation of a previously sent connect request, called a connect confirmation, has arrived at the transport endpoint. The confirmation is generated when a server accepts a connect request.

User data has arrived at the transport endpoint.

Expedited user data has arrived at the transport endpoint. Expedited data will be discussed later in this section.

A notification that the connection was aborted or that the server rejected a connect request, called a disconnect indication, has arrived at the transport endpoint.

A request for the orderly release of a connection, called an orderly release indication, has arrived at the transport endpoint.

The notification of an error in a previously sent datagram, called a unitdata error indication, has arrived at the transport endpoint (see the section ``Connectionless-mode service'').

Flow control restrictions on normal data flow that led to a TFLOW error have been lifted. Normal data may be sent again.

Flow control restrictions on expedited data flow that led to a TFLOW error have been lifted. Expedited data may be sent again.
It is possible in some states to receive one of several asynchronous events, as described in the state tables in ``State transitions''. The t_look(S) routine enables a user to determine what event has occurred if a TLOOK error is returned. The user can then process that event accordingly. In the example, if a connect request is rejected, the event passed to the client will be a disconnect indication. The client will exit if its request is rejected.

The server

Returning to the example, when the client calls t_connect, a connect indication will be generated on the server's listening transport endpoint. The steps required by the server to process the event are discussed below. For each client, the server accepts the connect request and spawns a server process to manage the connection.

   if ((call = (struct t_call *)t_alloc(listen_fd, T_CALL, T_ALL)) == NULL) {
   	t_error("t_alloc of t_call structure failed");

while (1) { if (t_listen(listen_fd, call) < 0) { t_error("t_listen failed for listen_fd"); exit(6); }

if ((conn_fd = accept_call(listen_fd, call)) != DISCONNECT) run_server(listen_fd); }

The server will loop forever, processing each connect indication. First, the server calls t_listen to retrieve the next connect indication. When one arrives, the server calls accept_call to accept the connect request. accept_call accepts the connection on an alternate transport endpoint (as discussed below) and returns the value of that endpoint. conn_fd is a global variable that identifies the transport endpoint where the connection is established. Because the connection is accepted on an alternate endpoint, the server may continue listening for connect indications on the endpoint that was bound for listening. If the call is accepted without error, run_server will spawn a process to manage the connection.

The server allocates a t_call structure to be used by t_listen. The third argument to t_alloc, T_ALL, specifies that all necessary buffers should be allocated for retrieving the caller's address, options, and user data. As mentioned earlier, the transport provider in this example does not support the transfer of user data during connection establishment, and also does not support any protocol options. Therefore, t_alloc will not allocate buffers for the user data and options. It must, however, allocate a buffer large enough to store the address of the caller. t_alloc determines the buffer size from the addr characteristic returned by t_open. The maxlen member of each netbuf structure will be set to the size of the newly allocated buffer by t_alloc (maxlen is 0 for the user data and options buffers).

Using the t_call structure, the server calls t_listen to retrieve the next connect indication. If one is currently available, it is returned to the server immediately. Otherwise, t_listen will block until a connect indication arrives.

NOTE: The Transport Interface supports an asynchronous mode for these routines, which prevents a process from blocking. This feature is discussed in the section ``Advanced topics''.

When a connect indication arrives, the server calls accept_call to accept the client's request, as follows:

   accept_call(listen_fd, call)
   int listen_fd;
   struct t_call *call;
   	int resfd;

if ((resfd = t_open("/dev/ticots", O_RDWR, NULL)) < 0) { t_error("t_open for responding fd failed"); exit(7); }

if (t_bind(resfd, NULL, NULL) < 0) { t_error("t_bind for responding fd failed"); exit(8); }

if (t_accept(listen_fd, resfd, call) < 0) { if (t_errno == TLOOK) { /* must be a disconnect */ if (t_rcvdis(listen_fd, NULL) < 0) { t_error("t_rcvdis failed for listen_fd"); exit(9); } if (t_close(resfd) < 0) { t_error("t_close failed for responding fd"); exit(10); } /* go back up and listen for other calls */ return(DISCONNECT); } t_error("t_accept failed"); exit(11); } return(resfd); }

accept_call takes two arguments.

The server first establishes another transport endpoint by opening the clone device node of the transport provider and binding an address. As with the client, a NULL value is passed to t_bind to specify that the user does not care what address is bound by the provider. The newly established transport endpoint, resfd, is used to accept the client's connect request.

NOTE: The server does not have to use t_bind after t_open and before t_accept. In this case, t_accept will execute the binding as well, and resfd will appear to be bound to the same (local) address as listen_fd. This alternate approach is recommended for protocol-independent applications.

The first two arguments of t_accept specify the listening transport endpoint and the endpoint where the connection will be accepted, respectively. A connection may be accepted on the listening endpoint, but this prevents other clients from accessing the server for the duration of the connection.

The third argument of t_accept points to the t_call structure associated with the connect indication. This structure should contain the address of the calling user and the sequence number returned by t_listen. The value of sequence is significant if the server manages multiple outstanding connect indications. ``Advanced topics'' presents an example of this situation. Also, the t_call structure should identify protocol options the user would like to specify, and user data that may be passed to the client. Because the transport provider in this example does not support protocol options or the transfer of user data during connection establishment, the t_call structure returned by t_listen may be passed without change to t_accept.

For simplicity in the example, the server will exit if either the t_open or t_bind call fails. exit(S) will close the transport endpoint associated with listen_fd, causing the transport provider to pass a disconnect indication to the client that requested the connection. This disconnect indication notifies the client that the connection was not established; t_connect will fail, setting t_errno to TLOOK.

t_accept may fail if an asynchronous event has occurred on the listening transport endpoint before the connection is accepted, and t_errno will be set to TLOOK. The state transition table in ``State transitions'' shows that the only event that may occur in this state with only one outstanding connect indication is a disconnect indication. This event may occur if the client decides to undo the connect request it had previously sent. If a disconnect indication arrives, the server must retrieve the disconnect indication using t_rcvdis. This routine takes a pointer to a t_discon structure as an argument, which is used to retrieve information associated with a disconnect indication. In this example, however, the server does not care to retrieve this information, so it sets the argument to NULL.

After receiving the disconnect indication, accept_call closes the responding transport endpoint and returns DISCONNECT, which informs the server that the connection was disconnected by the client. The server then listens for further connect indications.

``Listening and responding transport endpoints'' illustrates how the server establishes connections.

Listening and responding transport endpoints

The transport connection is established on the newly created responding endpoint, and the listening endpoint is freed to retrieve further connect indications.

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005