Add Book to My BookshelfPurchase This Book Online

Chapter 15 - Networking with TLI

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

Connection-Oriented Service
Connection-oriented service is more complicated than connectionless service, just as it was for the socket interface.
Server-Side Functions
To be a server, a process must inform the operating system that it wishes to receive connections, and then process those connection requests as they come in.
Waiting for connections
Unlike the socket interface, in which the server calls listen once and then loops on calls to accept to be notified of incoming connections, in TLI the server loops on calls to t_listen:
    #include <tiuser.h>
    int t_listen(int fd, struct t_call *call);
This function will block until a connection request arrives on the transport endpoint referenced by fd. When a connection request arrives, a description of the request will be placed in call, a pointer to a structure of type struct t_call:
    struct t_call {
        struct netbuf    addr;
        struct netbuf    opt;
        struct netbuf    udata;
        int              sequence;
    };
The maxlen field of addr, opt, and udata must be set before the call to t_listen. On return, addr will contain the address of the caller, opt will contain any protocol-specific options associated with the request, and udata will contain any data sent by the caller in the connection request (if the transport provider supports this). The sequence field will uniquely identify the connection request, allowing a server to listen for multiple connection requests before responding to any of them.
On success, t_listen returns 0. If a failure occurs, it returns -1 and the error indication is stored in t_errno (and perhaps errno).
Accepting and rejecting connections
Once a connection request has been received via t_listen, the server can either accept or reject that request. To accept the request, the server calls the t_accept function:
    #include <tiuser.h>
    int t_accept(int fd, int resfd, struct t_call *call);
The fd parameter refers to the transport endpoint, and the call parameter should be a pointer to the struct t_call structure returned by t_listen.
If resfd is equal to fd, the connection will be accepted on the same transport endpoint from which it arrived. This is permissible only when there are no outstanding connection indications on the endpoint that have not been responded to. If resfd is not equal to fd, it should refer to another bound endpoint that will be used to accept the connection. This allows the server to continue to receive connection requests on the original endpoint (which is the desired behavior for servers using well-known ports).
To reject a connection request, the server uses the t_snddis function:
    #include <tiuser.h>
    int t_snddis(int fd, struct t_call *call);
The fd parameter is the transport endpoint, and call should point to the struct t_call structure returned by t_listen.
Both t_accept and t_snddis return 0 on success, and -1 on failure. If an error occurs, its indication will be placed in t_errno (and perhaps errno).
Client-Side Functions
Before it can transfer data, a client program must connect to the server. To do this, it uses the t_connect function:
    #include <tiuser.h>
    int t_connect(int fd, struct t_call *sndcall,
            struct t_call *rcvcall);
The fd parameter refers to a bound transport endpoint. The sndcall and rcvcall parameters point to structures of type t_call (see above).
In sndcall, addr is the address of the server to connect to, opt contains any protocol-specific options, and udata may contain data to be transmitted along with the connection request if the transport provider supports this.
In rcvcall, the maxlen field of the struct netbuf structures must be set before the call. On return, the addr field will contain the address of the remote end of the connection, opt will contain any protocol-specific options, and udata will contain any data returned with the connection establishment or rejection. If rcvcall is NULL, no information will be returned.
If the connection request is rejected by the server, t_connect will fail with t_errno set to TLOOK. In this case, the client should then call t_rcvdis:
    #include <tiuser.h>
    int t_rcvdis(int fd, struct t_discon *discon);
The fd parameter specifies the transport endpoint, and the discon parameter points to a structure of type struct t_discon, which will contain the reason for rejection:
    struct t_discon {
        struct netbuf    udata;
        int              reason;
        int              sequence;
    };
The udata field will contain any data sent by the server along with the rejection. The reason parameter specifies an implementation-specific reason for the rejection, and sequence is unused in this case. If the client is not interested in the reason for rejection it can specify discon as NULL, but it must still make the call to t_rcvdis.
Both t_connect and t_rcvdis return 0 on success, and -1 on failure. If the operation fails, t_errno (and perhaps errno) will contain the error indication.
Transferring Data
Once a connection has been established, the client and server can exchange data using the t_snd and t_rcv functions:
    #include <tiuser.h>
    int t_snd(int fd, char *buf, unsigned nbytes, int flags);
    int t_rcv(int fd, char *buf, unsigned nbytes, int *flags);
In both cases, fd is the transport endpoint. In t_snd, buf is the data to be transferred, and nbytes is the number of bytes to be transferred. In t_rcv, buf is the buffer in which to store received data, and nbytes specifies the size of the buffer.
In t_snd, the flags parameter specifies the following options on the send:
T_EXPEDITED
Send the data as expedited (out-of-band) data instead of as normal data.
T_MORE
The current TSDU is being sent in multiple t_snd calls. Each call with T_MORE set appends to the current TSDU; when a send without this flag is executed, the TSDU is sent.
In t_rcv, flags points to a flags word that will be modified to contain any flags from the call to t_snd.
On successful completion, t_snd and t_rcv return the number of bytes sent or received. On failure, they return -1 and store the error indication in t_errno (and perhaps errno).
Connection Release
If the connection supports orderly release, the server and client must negotiate the orderly release of the connection. This is done with the t_sndrel and t_rcvrel functions:
    #include <tiuser.h>
    int t_sndrel(int fd);
    int t_rcvrel(int fd);
When the client or server has nothing more to send, it should call t_sndrel. When the client or server receives the notification of this (see below), it should call t_rcvrel to acknowledge its receipt. To shut down the connection completely in both directions, both sides should eventually call both of these functions.
Both of these functions return 0 on success, and -1 on failure. If they fail, an error indication will be stored in t_errno (and perhaps errno).
Examples 15-3 and 15-4 show reimplementations of the client and server programs from Examples 14-1 and 14-2 using TLI. These two programs exchange data using a virtual circuit.
Example 15-3:  server
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netconfig.h>
#include <tiuser.h>
#include <netdir.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#define PORTNUMBER  12345
extern int t_errno;
int
main(void)
{
    int n, fd, flags;
    struct t_call *callp;
    struct netconfig *ncp;
    struct nd_hostserv ndh;
    struct nd_addrlist *nal;
    struct t_bind *reqp, *retp;
    char buf[1024], hostname[64];
    /*
     * Get our local host name.
     */
    if (gethostname(hostname, sizeof(hostname)) < 0) {
        perror("gethostname");
        exit(1);
    }
    /*
     * Select the TCP transport provider.
     */
    if ((ncp = getnetconfigent("tcp")) == NULL) {
        nc_perror("tcp");
        exit(1);
    }
    /*
     * Get a host and service address for our host.  Since our
     * port number is not registered in the services file, we
     * send down the ASCII string representation of it.
     */
    sprintf(buf, "%d", PORTNUMBER);
    ndh.h_host = hostname;
    ndh.h_serv = buf;
    if (netdir_getbyname(ncp, &ndh, &nal) != 0) {
        netdir_perror(hostname);
        exit(1);
    }
    /*
     * Create a transport endpoint.
     */
     if ((fd = t_open(ncp->nc_device, O_RDWR, NULL)) < 0) {
        t_error("t_open");
        exit(1);
     }
    /*
     * Bind the address to the transport endpoint.
     */
    retp = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
    reqp = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
    if (reqp == NULL || retp == NULL) {
        t_error("t_alloc");
        exit(1);
    }
   
    memcpy(&reqp->addr, &nal->n_addrs[0], sizeof(struct netbuf));
    reqp->qlen = 5;
    if (t_bind(fd, reqp, retp) < 0) {
        t_error("t_bind");
        exit(1);
    }
    if (retp->addr.len != nal->n_addrs[0].len ||
        memcmp(retp->addr.buf, nal->n_addrs[0].buf, retp->addr.len) != 0) {
        fprintf(stderr, "did not bind requested address.\n");
        exit(1);
    }
    /*
     * Allocate a call structure.
     */
    callp = (struct t_call *) t_alloc(fd, T_CALL, T_ALL);
    if (callp == NULL) {
        t_error("t_alloc");
        exit(1);
    }
    /*
     * Listen for a connection.
     */
    if (t_listen(fd, callp) < 0) {
        t_error("t_listen");
        exit(1);
    }
    /*
     * Accept a connect on the same file descriptor used for listening.
     */
    if (t_accept(fd, fd, callp) < 0) {
        t_error("t_accept");
        exit(1);
    }
    /*
     * Read from the network until end-of-file and
     * print what we get on the standard output.
     */
    while ((n = t_rcv(fd, buf, sizeof(buf), &flags)) > 0)
        write(1, buf, n);
    /*
     * Release the connection.
     */
    t_rcvrel(fd);
    t_sndrel(fd);
    t_unbind(fd);
    t_close(fd);
    exit(0);
}
Example 15-4:  client
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netconfig.h>
#include <tiuser.h>
#include <netdir.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#define PORTNUMBER  12345
extern int t_errno;
int
main(void)
{
    int n, fd;
    struct t_call *callp;
    struct netconfig *ncp;
    struct nd_hostserv ndh;
    struct nd_addrlist *nal;
    char buf[32], hostname[64];
    /*
     * Get our local host name.
     */
    if (gethostname(hostname, sizeof(hostname)) < 0) {
        perror("gethostname");
        exit(1);
    }
    /*
     * Select the TCP transport provider.
     */
    if ((ncp = getnetconfigent("tcp")) == NULL) {
        nc_perror("tcp");
        exit(1);
    }
    /*
     * Get a host and service address for our host.  Since our
     * port number is not registered in the services file, we
     * send down the ASCII string representation of it.
     */
    sprintf(buf, "%d", PORTNUMBER);
    ndh.h_host = hostname;
    ndh.h_serv = buf;
    if (netdir_getbyname(ncp, &ndh, &nal) != 0) {
        netdir_perror(hostname);
        exit(1);
    }
    /*
     * Create a transport endpoint.
     */
     if ((fd = t_open(ncp->nc_device, O_RDWR, NULL)) < 0) {
        t_error("t_open");
        exit(1);
     }
     /*
      * Bind an arbitrary address to the transport
      * endpoint.
      */
     if (t_bind(fd, NULL, NULL) < 0) {
        t_error("t_bind");
        exit(1);
     }
    /*
     * Allocate a connection structure.
     */
    callp = (struct t_call *) t_alloc(fd, T_CALL, 0);
    if (callp == NULL) {
        t_error("t_alloc");
        exit(1);
    }
    /*
     * Construct the connection request.
     */
    memcpy(&callp->addr, &nal->n_addrs[0], sizeof(struct netbuf));
    /*
     * Connect to the server.
     */
    if (t_connect(fd, callp, NULL) < 0) {
        if (t_errno == TLOOK) {
            if (t_rcvdis(fd, NULL) < 0) {
                t_error("t_rcvdis");
                exit(1);
            }
        }
        else {
            t_error("t_connect");
            exit(1);
        }
    }
       
    /*
     * Read from standard input, and copy the
     * data to the network.
     */
    while ((n = read(0, buf, sizeof(buf))) > 0) {
        if (t_snd(fd, buf, n, 0) < 0) {
            t_error("t_snd");
            exit(1);
        }
    }
    /*
     * Release the connection.
     */
    t_sndrel(fd);
    t_rcvrel(fd);
    t_unbind(fd);
    t_close(fd);
    exit(0);
}
    % server &
    % client < /etc/motd
    Sun Microsystems Inc.   SunOS 5.3       Generic September 1993
Examples 15-5 and 15-6 show the same programs as they are implemented in HP-UX 10.x. The primary differences are as follows:
 1.The function gethostbyname is used rather than netdir_getbyname to get the host address, and the port number is already known. The gethostbyname function is described in Chapter 14, Networking With Sockets.
 2.Rather than using getnetconfigent to obtain the name of a suitable network device for use with t_open, the device name is specified in the source code. In this case, /dev/inet_cots provides a connection-oriented transport service using the Internet protocol suite (TCP/IP).
 3.Instead of using a transport-independent struct nd_addrlist structure for handling network addresses, a struct sockaddr_in structure (specific to the Internet protocol domain) is used. Creating the host and service address for the host is the same as with the socket interface.
Example 15-5:  server
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <tiuser.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#define PORTNUMBER  12345
extern int t_errno;
int
main(void)
{
    int n, fd, flags;
    struct t_call *callp;
    struct t_bind *reqp, *retp;
    struct sockaddr_in loc_addr;
    char buf[1024], hostname[64];
    /*
     * Get our local host name.
     */
    if (gethostname(hostname, sizeof(hostname)) < 0) {
        perror("gethostname");
        exit(1);
    }
    /*
     * Create a host and service address for our host.
     */
    memset((char *) &loc_addr, 0, sizeof(struct sockaddr_in));
    loc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    loc_addr.sin_port = htons(PORTNUMBER);
    loc_addr.sin_family = AF_INET;
    /*
     * Create a transport endpoint.
     */
     if ((fd = t_open("/dev/inet_cots", O_RDWR, NULL)) < 0) {
        t_error("t_open");
        exit(1);
     }
    /*
     * Bind the address to the transport endpoint.
     */
    retp = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
    reqp = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
    if (reqp == NULL || retp == NULL) {
        t_error("t_alloc");
        exit(1);
    }
   
    reqp->addr.maxlen = sizeof(struct sockaddr_in);
    reqp->addr.len = sizeof(struct sockaddr_in);
    reqp->addr.buf = (char *) &loc_addr;
    reqp->qlen = 5;
    if (t_bind(fd, reqp, retp) < 0) {
        t_error("t_bind");
        exit(1);
    }
    if (retp->addr.len != reqp->addr.len ||
        memcmp(retp->addr.buf, reqp->addr.buf, retp->addr.len) != 0) {
        fprintf(stderr, "did not bind requested address.\n");
        exit(1);
    }
    /*
     * Allocate a call structure.
     */
    callp = (struct t_call *) t_alloc(fd, T_CALL, T_ALL);
    if (callp == NULL) {
        t_error("t_alloc");
        exit(1);
    }
    /*
     * Listen for a connection.
     */
    if (t_listen(fd, callp) < 0) {
        t_error("t_listen");
        exit(1);
    }
    /*
     * Accept a connect on the same file descriptor used for listening.
     */
    if (t_accept(fd, fd, callp) < 0) {
        t_error("t_accept");
        exit(1);
    }
    /*
     * Read from the network until end-of-file and
     * print what we get on the standard output.
     */
    while ((n = t_rcv(fd, buf, sizeof(buf), &flags)) > 0)
        write(1, buf, n);
    /*
     * Release the connection.
     */
    t_rcvrel(fd);
    t_sndrel(fd);
    t_unbind(fd);
    t_close(fd);
    exit(0);
}
Example 15-6:  client
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <tiuser.h>
#include <string.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#define PORTNUMBER  12345
extern int t_errno;
int
main(void)
{
    int n, fd;
    struct hostent *hp;
    struct t_call *callp;
    char buf[32], hostname[64];
    struct sockaddr_in rem_addr;
    /*
     * Get our local host name.
     */
    if (gethostname(hostname, sizeof(hostname)) < 0) {
        perror("gethostname");
        exit(1);
    }
    /*
     * Get the address of our host.
     */
    if ((hp = gethostbyname(hostname)) == NULL) {
        fprintf(stderr, "Cannot find address for %s\n", hostname);
        exit(1);
    }
    /*
     * Create a host and service address for our host.
     */
    memset((char *) &rem_addr, 0, sizeof(struct sockaddr_in));
    memcpy((char *) &rem_addr.sin_addr.s_addr, (char *) hp->h_addr,
           hp->h_length);
    rem_addr.sin_port = htons(PORTNUMBER);
    rem_addr.sin_family = AF_INET;
    /*
     * Create a transport endpoint.
     */
     if ((fd = t_open("/dev/inet_cots", O_RDWR, NULL)) < 0) {
        t_error("t_open");
        exit(1);
     }
     /*
      * Bind an arbitrary address to the transport
      * endpoint.
      */
     if (t_bind(fd, NULL, NULL) < 0) {
        t_error("t_bind");
        exit(1);
     }
    /*
     * Allocate a connection structure.
     */
    callp = (struct t_call *) t_alloc(fd, T_CALL, T_ADDR);
    if (callp == NULL) {
        t_error("t_alloc");
        exit(1);
    }
    /*
     * Construct the connection request.
     */
    callp->addr.maxlen = sizeof(struct sockaddr_in);
    callp->addr.len = sizeof(struct sockaddr_in);
    callp->addr.buf = (char *) &rem_addr;
    callp->udata.len = 0;
    callp->opt.len = 0;
    /*
     * Connect to the server.
     */
    if (t_connect(fd, callp, NULL) < 0) {
        if (t_errno == TLOOK) {
            if (t_rcvdis(fd, NULL) < 0) {
                t_error("t_rcvdis");
                exit(1);
            }
        }
        else {
            t_error("t_connect");
            exit(1);
        }
    }
       
    /*
     * Read from standard input, and copy the
     * data to the network.
     */
    while ((n = read(0, buf, sizeof(buf))) > 0) {
        if (t_snd(fd, buf, n, 0) < 0) {
            t_error("t_snd");
            exit(1);
        }
    }
    /*
     * Release the connection.
     */
    t_sndrel(fd);
    t_rcvrel(fd);
    t_unbind(fd);
    t_close(fd);
    exit(0);
}

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback