/**********************************************************************/
/*                                                                    */
/* Copyright (c) 2001 by Sun Microsystems, Inc.                       */
/* All rights reserved.                                               */
/*                                                                    */
/**********************************************************************/

/*****************************************************************************
*
* Prog Name:    sock00.c
*
* Description:
*
*    Stream Socket Sample Program - send and receive. This TP is partnered
*    with transaction SOCK, initial program SOCK00, source SOCK00.cl2
*
*    This program opens a socket and connects to <host><port>. It
*    invokes the transaction by sending a message to the socket of the
*    form:
*
*    xxxx,12345678901234567890123456789012345
*
*    where,
*
*    "xxxx" is the tran id of 1 to 4 characters (SOCK)
*
*    "12345.......45" is up to 35 characters of optional data  
*    to be placed in the COMMAREA in CLIENT-IN-DATA.
*
*    Having sent its initial message the program must receive a 
*    message on the socket. This shows the transaction has been 
*    initiated at the Sun MTP system.
*
*    For each message received a new message will be transmitted until
*    the socket is closed by the remote transaction.
*
* Usage:
*
*       sock -h host [-p port]
*
*       where,
*
*       -h host   identifies the socket Server host.
*       -p port   identifies the socket Server port.
*
* Change Log:
*
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
 
#define BUFSIZE    4096    /* size of messages buffer */

/*
 * Read "n" bytes from a descriptor.
 * Use in place of read() when fd is a stream socket.
 * Return number of bytes actually read.
 */
int readn(fd, buf, nbytes)
int  fd;
char *buf;
int  nbytes;
{
    int	nleft, n;

    nleft = nbytes;
    while (nleft > 0) {
	if ((n = recv(fd, buf, nleft, 0)) < 0) {
	    perror("recv");
	    return n;  /* error */
	} else if (n == 0) {
	    break;     /* EOF */
	}

	nleft -= n;
	buf   += n;
    }

    return (nbytes - nleft);
}

/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 * Return number of bytes actually written.
 */
int writen(fd, buf, nbytes)
int  fd;
char *buf;
int  nbytes;
{
    int	nleft, n;

    nleft = nbytes;
    while (nleft > 0) {
	if ((n = send(fd, buf, nleft, 0)) <= 0) {
	    perror("send");
	    return n;  /* error */
	}

	nleft -= n;
	buf   += n;
    }

    return (nbytes - nleft);
}


usage(progname)
char *progname;
{
    printf("usage: %s -h host [-p port]\n", progname);
    exit(EXIT_FAILURE);
}

main(argc, argv)
int  argc;
char *argv[];
{
    char *               host = NULL;
    int                  port = 8001;
    char *               trans = "SOCK";
    int                  fd;
    struct hostent     * hostentry = NULL;
    struct sockaddr_in   addr;
    int                  n;
    int                  c, errflg = 0;
    char                 ibuffer[4+1+35];  /* transid  4 bytes
				              ','      1 byte
				              data    35 bytes */
    char                 buffer[BUFSIZE];

    char * data = {"12345678901234567890123456789012345678901234567890"};

    signal(SIGPIPE, SIG_IGN);

    while ((c=getopt(argc, argv, "h:p:")) != -1) {
        switch(c) {
	case 'h':	/* server host name */
	    host = optarg;
	    break;
        case 'p':	/* server port */
            port = atoi(optarg);
            break;
        default:	/* the rest are errors */
            errflg++;
            break;
        }
    }
 
    if (host == NULL || errflg) {
        usage(argv[0]);
    }

    /*  establish socket */
    if ((fd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
	printf("sock:socket error %d\n", errno);
        exit(EXIT_FAILURE);           
    }

    printf("sock:socket allocated %d\n", fd);
    
    /* connect to socket server */
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(port);

    if (strcmp(host, "any") == 0) {
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } else if (strcmp(host, "loopback") == 0) {
	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    } else {
	if ((hostentry = gethostbyname(host)) == NULL) {
	    printf("sock:invalid Host Name \"%s\"\n", host);
	    exit(EXIT_FAILURE);
	}
	memcpy(&addr.sin_addr.s_addr, hostentry->h_addr, hostentry->h_length);
    }                      

    printf("sock:host address %d.%d.%d.%d : port %d\n", 
	(addr.sin_addr.s_addr >> 24) & 0xFF,
	(addr.sin_addr.s_addr >> 16) & 0xFF,
	(addr.sin_addr.s_addr >>  8) & 0xFF,
	(addr.sin_addr.s_addr      ) & 0xFF,
	port);

    if (connect(fd, (struct sockaddr *)&addr,
		sizeof(struct sockaddr)) < 0) {
	printf("sock:connect error %d\n", errno);
        exit(EXIT_FAILURE);
    }

    printf("sock:socket bound\n");

    /* build initial socket message */
    memset(ibuffer, 0, sizeof(ibuffer));
    memcpy(ibuffer,   trans,  4);
    memcpy(ibuffer+4, ",",    1);
    memcpy(ibuffer+5, data,  35);

    /* Initial send to invoke transaction */
    if (send(fd, ibuffer, sizeof(ibuffer), 0) <= 0) {
	printf("sock:send error %d\n", errno);
	exit(EXIT_FAILURE);
    }

    while (1) {

            memset(buffer, 0, sizeof(buffer));

            if ((n=readn(fd, buffer, sizeof(buffer))) < 0) {
		printf("sock:read error %d\n", errno);
		exit(EXIT_FAILURE);
	    }
	    
	    if (n == 0) {
		printf("sock:peer disconnected\n", n);
		break;
	    }

	    printf("sock:read %d chars from peer\n", n);

            if ((n=writen(fd, (char *)buffer, n)) <= 0) {
		printf("sock:send error %d\n", errno);
		exit(EXIT_FAILURE);
	    }

	    printf("sock:sent %d chars to peer\n", n);
    }

    printf("sock:done, exiting...\n", n);
    close(fd);
    return EXIT_SUCCESS;
}

