|
|
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:
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.
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.
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>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:#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); }
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.
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).)
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 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>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:#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); }
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.
If t_bind succeeds, the provider will begin queueing connect indications. The next phase of communication is .``Connection establishment''connection establishment