Programming with the X/Open Transport Interface (XTI)

Local management

Before the client and server can establish a transport connection, each must first establish a local channel (the transport endpoint) to the transport provider using t_open, and establish its identity (or address) using t_bind.

The set of services supported by the Transport Interface may not be implemented by all transport protocols. Each transport provider has a set of characteristics that determine the services it offers and the limits associated with those services. This information is returned to the user in the info argument by t_open, and consists of the following:

maximum size of a transport address

maximum number of bytes of protocol-specific options that may be passed between the transport user and transport provider

maximum message size that may be transmitted in either connection-mode or connectionless-mode

maximum expedited data message size that may be sent over a transport connection

maximum number of bytes of user data that may be passed between users during connection establishment

maximum bytes of user data that may be passed between users during the abortive release of a connection

the type of service supported by the transport provider

other information about the transport provider

The three service types defined by the Transport Interface are listed below. Only one such service can be associated with the transport provider identified by t_open.

The transport provider supports connection-mode service but does not provide the optional orderly release facility.

The transport provider supports connection-mode service with the optional orderly release facility.

The transport provider supports connectionless-mode service.

NOTE: t_open returns the default provider characteristics associated with a transport endpoint. However, some characteristics may change after an endpoint has been opened. This will occur if the characteristics are associated with negotiated options (option negotiation is described later in this section). For example, if the support of expedited data transfer is a negotiated option, the value of this characteristic may change. t_getinfo may be called to retrieve the current characteristics of a transport endpoint.

Once a user establishes a transport endpoint with the chosen transport provider, it must establish its identity. As mentioned earlier, t_bind does this by binding a transport address to the transport endpoint. In addition, for servers, this routine informs the transport provider that the endpoint will be used to listen for incoming connect indications, also called connect requests.

The function t_optmgmt(S), is also available during the local management phase. It enables a user to negotiate the values of protocol options with the transport provider. Each transport protocol is expected to define its own set of negotiable protocol options, which may include such information as Quality-of-Service parameters. Because of the protocol-specific nature of options, only applications written for a particular protocol environment are expected to use this facility.

NOTE: Although t_optmgmt supports option management as described in the XTI specification, the UnixWare transport providers do not support all of the XTI options.

The client

The local management requirements of the example client and server are used to discuss details of these facilities. The following are the definitions needed by the client program, followed by its necessary local management steps.

   #include <stdio.h>
   #include <xti.h>
   #include <fcntl.h>

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

main() { int fd; int nbytes; int flags = 0; char buf[1024]; struct t_call *sndcall;

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

if (t_bind(fd, NULL, NULL) < 0) { t_error("t_bind failed"); exit(2); }

The first argument to t_open is the pathname of a file that identifies the transport protocol that will supply the transport service. In this example, /dev/ticots is a STREAMS ``clone'' device node that identifies a generic, connection-based transport protocol (see clone(M)). The clone device finds an available minor device of the transport provider for the user. It is opened for both reading and writing, as specified by the O_RDWR open flag. The third argument may be used to return the service characteristics of the transport provider to the user. This information is useful when writing protocol-independent software (discussed in ``Guidelines for protocol independence''). For simplicity, the client and server in this example ignore this information and assume the transport provider has the following characteristics:

Because these characteristics are not needed by the user, NULL is specified in the third argument to t_open. If the user needed a service type other than T_COTS, another transport provider would be opened. An example of the T_CLTS service invocation is presented in the section ``Connectionless-mode service''.

The return value of t_open is an identifier for the transport endpoint that will be used by all subsequent Transport Interface function calls. This identifier is actually a file descriptor obtained by opening the transport protocol file (see open(S)). The significance of this fact is highlighted in the section ``A read/write interface''.

After the transport endpoint is created, the client calls t_bind to assign an address to the endpoint. The first argument identifies the transport endpoint. The second argument describes the address the user would like to bind to the endpoint, and the third argument is set on return from t_bind to specify the address that the provider bound.

The address associated with a server's transport endpoint is important, because that is the address used by all clients to access the server. However, the typical client does not care what its own address is, because no other process will try to access it. That is the case in this example, where the second and third arguments to t_bind are set to NULL. A NULL second argument directs the transport provider to choose an address for the user. A NULL third argument specifies that the user does not care what address was assigned to the endpoint.

NOTE: The function t_getprotaddr(S) can be used to find out the local address to which the user is bound. The same function can also provide the remote address, in case a connection has been established.

If either t_open or t_bind fails, the program will call t_error(S) to print an appropriate error message to stderr. If any Transport Interface routine fails, the global integer t_errno will be assigned a transport error value. A set of error values has been defined (in xti.h) for the Transport Interface, and t_error will print an error message corresponding to the value in t_errno. This routine is analogous to perror(3C), which prints an error message based on the value of errno. If the error associated with a transport function is a system error, t_errno will be set to TSYSERR, and errno will be set to the appropriate value.

The function t_strerror(S) can also be used in place of t_error(S). t_strerror, introduced in XTI, takes as argument a valid value of t_errno, and returns a pointer to a string containing the corresponding error message. Both t_error and t_strerror return the message corresponding to the locale defined by the environment variable LANG. (See environ(M).)

NOTE: In applications linked with the Threads Library, a distinct instance of t_errno is supported for each thread. The value of t_errno for the current thread is returned by the new function get_t_errno(3sock). You may set the value of t_errno, (although doing so is not encouraged) by calling the function set_t_errno, which is also described on the get_t_errno(3sock) manual page. The symbol t_errno is still available and may be assigned values. However, users are encouraged not to do so. One reason is that storage for thread-specific t_errno is not guaranteed to exist in UnixWare. In conditions of low memory, a common location is used by all threads (except the first), so that the value of t_errno that a thread retrieves in such a situation may not be the value that it has most recently stored.

You should be aware that the functions get_t_errno and set_t_errno have not been approved by X/Open, so their use will make programs less portable.

See ``Multithreaded network programming'' for more information about using the Transport Interface with the Threads Library.

The server

The server in this example must take similar local management steps before communication can begin. The server must establish a transport endpoint through which it will listen for connect indications. The necessary definitions and local management steps are shown below:

   #include <xti.h>
   #include <stropts.h>
   #include <fcntl.h>
   #include <stdio.h>
   #include <signal.h>

#define DISCONNECT (-1) #define SRV_ADDR 1 /* server's well known address */

int conn_fd; /* connection established here */

main() {

int listen_fd; /* listening transport endpoint */ struct t_bind *bind; struct t_call *call;

if ((listen_fd = t_open("/dev/ticots", O_RDWR, NULL)) < 0) { t_error("t_open failed for listen_fd"); exit(1); }

/* * By assuming that the address is an integer value, * this program may not run over another protocol. */

if ((bind = (struct t_bind *)t_alloc(listen_fd, T_BIND, T_ALL)) == NULL) { t_error("t_alloc of t_bind structure failed"); exit(2); }

bind->qlen = 1; bind->addr.len = sizeof(int); *(int *)bind->addr.buf = SRV_ADDR;

if (t_bind(listen_fd, bind, NULL) < 0) { t_error("t_bind failed for listen_fd"); exit(3); }

As with the client, the first step is to call t_open to establish a transport endpoint with the desired transport provider. This endpoint, listen_fd, will be used to listen for connect indications. Next, the server must bind its well-known address to the endpoint. This address is used by each client to access the server. The second argument to t_bind requests that a particular address be bound to the transport endpoint. This argument points to a t_bind structure with the following format:
   struct t_bind {
   	struct netbuf addr;
   	unsigned qlen;
where addr describes the address to be bound, and qlen specifies the maximum outstanding connect indications that may arrive at this endpoint. All Transport Interface structure and constant definitions are found in xti.h.

The address is specified using a netbuf structure that contains the following members:

   struct netbuf {
   	unsigned int maxlen;
   	unsigned int len;
   	char *buf;
where buf points to a buffer containing the data, len specifies the bytes of data in the buffer, and maxlen specifies the maximum bytes the buffer can hold (and need only be set when data is returned to the user by a Transport Interface routine). For the t_bind structure, the data pointed to by buf identifies a transport address. It is expected that the structure of addresses will vary among each protocol implementation under the Transport Interface. The netbuf structure is intended to support any address structure.

If the value of qlen is greater than 0, the transport endpoint may be used to listen for connect indications. In such cases, t_bind directs the transport provider to begin queueing connect indications destined for the bound address immediately. Furthermore, the value of qlen specifies the maximum outstanding connect indications the server wishes to process. The server must respond to each connect indication, either accepting or rejecting the request for connection. An outstanding connect indication is one to which the server has not yet responded. Often, a server will fully process a single connect indication and respond to it before receiving the next indication. When this occurs, a value of 1 is appropriate for qlen. However, some servers may want to retrieve several connect indications before responding to any of them. In such cases, qlen specifies the maximum number of outstanding indications the server will process. An example of a server that manages multiple outstanding connect indications is presented in the section ``Advanced topics''.

t_alloc(S) is called to allocate the t_bind structure needed by t_bind. t_alloc takes three arguments. The first is a file descriptor that references a transport endpoint. This is used to access the characteristics of the transport provider (see t_open(S)). The second argument identifies the appropriate Transport Interface structure to be allocated. The third argument specifies which, if any, netbuf buffers should be allocated for that structure. T_ALL specifies that all netbuf buffers associated with the structure should be allocated, and causes the addr buffer to be allocated in this example. The size of this buffer is determined from the transport provider characteristic that defines the maximum address size. The maxlen member of this netbuf structure will be set to the size of the newly allocated buffer by t_alloc. The use of t_alloc helps ensure the compatibility of user programs with future releases of the Transport Interface.

The server in this example processes connect indications one at a time, so qlen is set to 1. The address information is then assigned to the newly allocated t_bind structure. This t_bind structure passes information to t_bind in the second argument and returns information to the user in the third argument.

On return, the t_bind structure contains the address that was bound to the transport endpoint. In the TLI version of t_bind, if the provider cannot bind the requested address (perhaps because it has been bound to another transport endpoint), it will choose another appropriate address. However, the XTI version of t_bind will fail if the requested address cannot be bound. Therefore, it is not necessary in new applications to check that the requested address and bound address are the same after a successful call to t_bind.

NOTE: Each transport provider manages its address space differently. Some transport providers may allow a single transport address to be bound to several transport endpoints, while others may require a unique address per endpoint. The Transport Interface supports either choice. Based on its address management rules, a provider will determine if it can bind the requested address.

If t_bind succeeds, the provider will begin queueing connect indications. The next phase of communication is .``Connection establishment''connection establishment

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