Network Interface Guide

Connection Establishment

XTI/TLI imposes different procedures in this phase for clients and servers. The client starts connection establishment by requesting a connection to a specified server using t_connect(3NSL). The server receives a client's request by calling t_listen(3NSL). The server must accept or reject the client's request. It calls t_accept(3NSL) to establish the connection, or t_snddis(3NSL) to reject the request. The client is notified of the result when t_connect(3NSL) returns.

TLI supports two facilities during connection establishment that might not be supported by all transport providers:

These facilities produce protocol-dependent software (see "Guidelines to Protocol Independence").

Client

The steps for the client to establish a connection are shown in Example 3-5.


Example 3-5 Client-to-Server Connection

if ((sndcall = (struct t_call *) t_alloc(fd, T_CALL, T_ADDR))
      == (struct t_call *) NULL) {
   t_error("t_alloc failed");
   exit(3);
}

/*
 * Because it assumes it knows the format of the provider's
 * address, this program is transport-dependent
 */
sndcall->addr.len = sizeof(int);
*(int *) sndcall->addr.buf = SRV_ADDR;
if (t_connect( fd, sndcall, (struct t_call *) NULL) == -1 ) {
   t_error("t_connect failed for fd");
   exit(4);
}

The t_connect(3NSL) call connects to the server. The first argument of t_connect(3NSL) identifies the client's endpoint, and the second argument points to a t_call structure that identifies the destination server. This structure has the following format:

struct t_call {
 	struct netbuf addr;
 	struct netbuf opt;
 	struct netbuf udata;
 	int sequence;
}

addr identifies the address of the server, opt specifies protocol-specific options to the connection, and udata identifies user data that can be sent with the connect request to the server. The sequence field has no meaning for t_connect(3NSL). In this example, only the server's address is passed.

t_alloc(3NSL) allocates the t_call structure dynamically. The third argument of t_alloc(3NSL) is T_ADDR, which specifies that the system needs to allocate a netbuf buffer. The server's address is then copied to buf, and len is set accordingly.

The third argument of t_connect(3NSL) can be used to return information about the newly established connection, and can return any user data sent by the server in its response to the connect request. The third argument here is set to NULL by the client. The connection is established on successful return of t_connect(3NSL). If the server rejects the connect request, t_connect(3NSL) sets t_errnoto TLOOK.

Event Handling

The TLOOK error has special significance. TLOOK is set if an XTI/TLI routine is interrupted by an unexpected asynchronous transport event on the endpoint. TLOOK does not report an error with an XTI/TLI routine, but the normal processing of the routine is not done because of the pending event. The events defined by XTI/TLI are listed in Table 3-7.

Table 3-7 Asynchronous Endpoint Events

Name 

Description 

T_LISTEN

Connection request arrived at the transport endpoint 

T_CONNECT

Confirmation of a previous connect request arrived (generated when a server accepts a connect request)  

T_DATA

User data has arrived 

T_EXDATA

Expedited user data arrived 

T_DISCONNECT

Notice that an aborted connection or a rejected connect request arrived 

T_ORDREL

A request for orderly release of a connection arrived 

T_UDERR

Notice of an error in a previous datagram arrived. (See "Read/Write Interface".)

The state table in "State Transitions" shows which events can happen in each state. t_look(3NSL) lets a user determine what event has occurred if a TLOOK error is returned. In the example, if a connect request is rejected, the client exits.

Server

When the client calls t_connect(3NSL), a connect request is sent at the server's transport endpoint. For each client, the server accepts the connect request and spawns a process to service the connection.

if ((call = (struct t_call *) t_alloc(listen_fd, T_CALL, T_ALL))
      == (struct t_call *) NULL) {
   t_error("t_alloc of t_call structure failed");
   exit(5);
}
while(1) {
   if (t_listen( listen_fd, call) == -1) {
      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 allocates a t_call structure, then does a closed loop. The loop blocks on t_listen(3NSL) for a connect request. When a request 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 handle of that endpoint. (conn_fd is a global variable.) Because the connection is accepted on an alternate endpoint, the server can continue to listen on the original endpoint. If the call is accepted without error, run_server spawns a process to service the connection.

XTI/TLI supports an asynchronous mode for these routines that prevents a process from blocking. See "Advanced Topics".

When a connect request arrives, the server calls accept_call to accept the client's request, as Example 3-6 shows.


Note -

It is implicitly assumed that this server only needs to handle a single connection request at a time. This is not normally true of a server. The code required to handle multiple simultaneous connection requests is complicated because of XTI/TLI event mechanisms. See "Advanced Programming Example" for such a server.



Example 3-6 accept_call Function

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

   if ((resfd = t_open("/dev/exmp", O_RDWR, (struct t_info *) NULL))
         == -1) {
      t_error("t_open for responding fd failed");
      exit(7);
 	}
   if (t_bind(resfd,(struct t_bind *) NULL, (struct t_bind *NULL))
         == -1) {
      t_error("t_bind for responding fd failed");
      exit(8);
   }
   if (t_accept(listen_fd, resfd, call) == -1) {
      if (t_errno == TLOOK) {								/* must be a disconnect */
         if (t_rcvdis(listen_fd,(struct t_discon *) NULL) == -1) {
            t_error("t_rcvdis failed for listen_fd");
            exit(9);
         }
         if (t_close(resfd) == -1) {
            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() has two arguments:

listen_fd The file handle of the transport endpoint where the connect request arrived.
callPoints to a t_call structure that contains all information associated with the connect request

The server first opens another transport endpoint by opening the clone device special file of the transport provider and binding an address. A NULL specifies not to return the address bound by the provider. The new transport endpoint, resfd, accepts the client's connect request.

The first two arguments of t_accept(3NSL) specify the listening transport endpoint and the endpoint where the connection is accepted, respectively. Accepting a connection on the listening endpoint prevents other clients from accessing the server for the duration of the connection.

The third argument of t_accept(3NSL) points to the t_call structure containing the connect request. This structure should contain the address of the calling user and the sequence number returned by t_listen(3NSL). The sequence number is significant if the server queues multiple connect requests. The "Advanced Topics" shows an example of this. The t_call structure also identifies protocol options and user data to pass to the client. Because this transport provider does not support protocol options or the transfer of user data during connection, the t_call structure returned by t_listen(3NSL) is passed without change to t_accept(3NSL).

The example is simplified. The server exits if either the t_open(3NSL) or t_bind(3NSL) call fails. exit(2) closes the transport endpoint of listen_fd, causing a disconnect request to be sent to the client. The client's t_connect(3NSL) call fails, setting t_errno to TLOOK.

t_accept(3NSL) can fail if an asynchronous event occurs on the listening endpoint before the connection is accepted, and t_errno is set to TLOOK. Table 3-8 shows that only a disconnect request can be sent in this state with only one queued connect request. This event can happen if the client undoes a previous connect request. If a disconnect request arrives, the server must respond by calling t_rcvdis(3NSL). This routine argument is a pointer to a t_discon structure, which is used to retrieve the data of the disconnect request. In this example, the server passes a NULL.

After receiving a disconnect request, 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 requests.

Figure 3-4 illustrates how the server establishes connections:

Figure 3-4 Listening and Responding Transport Endpoints

Graphic

The transport connection is established on the new responding endpoint, and the listening endpoint is freed to retrieve further connect requests.