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.

Connectionless Service
Connectionless (datagram) service is the simplest of the two types of communication that can be performed with the TLI. After the client and server have created their transport endpoints and bound them to addresses, they can exchange data using the t_sndudata and t_rcvudata functions:
    #include <tiuser.h>
    int t_sndudata(int fd, struct t_unitdata *data);
    int t_rcvudata(int fd, struct t_unitdata *data, int *flags);
In both functions, fd is a transport endpoint, and data points to a structure of type struct t_unitdata:
    struct t_unitdata {
        struct netbuf    addr;
        struct netbuf    opt;
        struct netbuf    udata;
    };
In this structure, addr is the address to which the data is to be sent or from which it was received, opt contains any protocol-specific options associated with the data, and udata contains the data that was transferred. Notice that the maxlen field of all three of these structures must be set before calling t_rcvudata.
The flags parameter to t_rcvudata should point at an area in which flags can be set. This area should be initialized to 0. The only flag currently defined is T_MORE, which will be set if the size of the udata buffer is not large enough to retrieve all the available data. Subsequent calls to t_rcvudata can be used to retrieve the remaining data.
The t_sndudata and t_rcvudata functions return 0 on success, and -1 on failure. If a failure occurs, an error code will be stored in t_errno (and perhaps errno).
Some errors that occur when a program is receiving data interrupt the flow of data. In connectionless mode, the only error that does this is the failure of a previous attempt to send data with t_sndudata. If t_rcvudata fails and sets t_errno to TLOOK, the application must call t_rcvuderr to clear the error:
    #include <tiuser.h>
    int t_rcvuderr(int fd, struct t_uderr *uderr);
The struct t_uderr structure is defined as:
    struct t_uderr {
        struct netbuf    addr;
        struct netbuf    opt;
        long             error;
    };
The maxlen field of addr and opt must be set before the call. On return, addr will contain the address of the failed transmission, opt will contain any options associated with the transmission, and error will contain an implementation-dependent error code.
One has to wonder why, when using an inherently unreliable service in which datagrams may be lost or discarded, TLI's designers decided it was necessary to inform the user of this particular error condition (but not of others). There is little that can be done about it (since no indication of which datagram failed is provided, no retransmission can be done), and it complicates the implementation of connectionless service.
Example 15-1 shows a reimplementation of Example 14-3 using SVR4 TLI. This program contacts the “daytime” service, an Internet standard service that returns the local time as an ASCII string.
Example 15-1:  daytime
#include <netconfig.h>
#include <netdir.h>
#include <tiuser.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#define SERVICENAME "daytime"
extern int t_errno;
int
main(int argc, char **argv)
{
    int fd, flags;
    struct netconfig *ncp;
    struct nd_hostserv ndh;
    struct t_unitdata *udp;
    struct nd_addrlist *nal;
    if (argc < 2) {
        fprintf(stderr, "Usage: %s hostname [hostname...]\n", *argv);
        exit(1);
    }
    /*
     * Select the UDP transport provider.
     */
    if ((ncp = getnetconfigent("udp")) == NULL) {
        nc_perror("udp");
        exit(1);
    }
    while (--argc) {
        ndh.h_host = *++argv;
        ndh.h_serv = SERVICENAME;
        /*
         * Get a host and service address for this host.
         */
        if (netdir_getbyname(ncp, &ndh, &nal) != 0) {
            netdir_perror(*argv);
            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 datagram.
         */
        udp = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ALL);
        if (udp == NULL) {
            t_error("t_alloc");
            exit(1);
        }
        /*
         * Construct the datagram.
         */
        memcpy(&udp->addr, &nal->n_addrs[0], sizeof(struct netbuf));
        udp->udata.len = 1;
        /*
         * Send a packet to the server.
         */
        if (t_sndudata(fd, udp) < 0) {
            t_error("t_sndudata");
            exit(1);
        }
        /*
         * Receive a packet back.
         */
        if (t_rcvudata(fd, udp, &flags) < 0) {
            if (t_errno == TLOOK) {
                if (t_rcvuderr(fd, NULL) < 0) {
                    t_error("t_rcvuderr");
                    exit(1);
                }
            }
            else {
                t_error("t_rcvudata");
                exit(1);
            }
        }
        /*
         * Print the packet.
         */
        udp->udata.buf[udp->udata.len] = '\0';
        printf("%s: %s", *argv, udp->udata.buf);
        /*
         * Shut down the connection.
         */
        t_unbind(fd);
        t_close(fd);
    }
    exit(0);
}
    % daytime localhost
    localhost: Mon Mar 20 15:50:54 1995
Example 15-2 shows the same program as it is implemented in HP-UX 10.x. The primary differences are as follows:
 1.Rather than using netdir_getbyname to obtain a host/service address, getservbyname is used to get the service address (port number), and gethostbyname is used to get the host address. These functions are 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_clts provides a connectionless 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 similar to what we did when using the socket interface.
Example 15-2:  daytime
#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 SERVICENAME "daytime"
extern int t_errno;
int
main(int argc, char **argv)
{
    int fd, flags;
    struct hostent *hp;
    struct servent *sp;
    struct t_unitdata *udp;
    struct nd_addrlist *nal;
    struct sockaddr_in rem_addr;
    if (argc < 2) {
        fprintf(stderr, "Usage: %s hostname [hostname...]\n", *argv);
        exit(1);
    }
    if ((sp = getservbyname(SERVICENAME, "udp")) == NULL) {
        fprintf(stderr, "%s/udp: unknown service\n", SERVICENAME);
        exit(1);
    }
    while (--argc) {
        if ((hp = gethostbyname(*++argv)) == NULL) {
            fprintf(stderr, "%s: unknown host\n", *argv);
            continue;
        }
        /*
         * Create a transport endpoint.
         */
        if ((fd = t_open("/dev/inet_clts", 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 datagram.
         */
        udp = (struct t_unitdata *) t_alloc(fd, T_UNITDATA, T_ALL);
        if (udp == NULL) {
            t_error("t_alloc");
            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 = sp->s_port;
        rem_addr.sin_family = AF_INET;
        /*
         * Construct the datagram.
         */
        udp->addr.maxlen = sizeof(struct sockaddr_in);
        udp->addr.len = sizeof(struct sockaddr_in);
        udp->addr.buf = (char *) &rem_addr;
        udp->opt.buf = (char *) 0;
        udp->opt.maxlen = 0;
        udp->opt.len = 0;
        udp->udata.len = 1;
        /*
         * Send a packet to the server.
         */
        if (t_sndudata(fd, udp) < 0) {
            t_error("t_sndudata");
            exit(1);
        }
        /*
         * Receive a packet back.
         */
        if (t_rcvudata(fd, udp, &flags) < 0) {
            if (t_errno == TLOOK) {
                if (t_rcvuderr(fd, NULL) < 0) {
                    t_error("t_rcvuderr");
                    exit(1);
                }
            }
            else {
                t_error("t_rcvudata");
                exit(1);
            }
        }
        /*
         * Print the packet.
         */
        udp->udata.buf[udp->udata.len] = '\0';
        printf("%s: %s", *argv, udp->udata.buf);
        /*
         * Shut down the connection.
         */
        t_unbind(fd);
        t_close(fd);
    }
    exit(0);
}

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