Network Interface Guide

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.