              <<< LASSIE::UCXAXP$DKA0:[NOTES$LIBRARY]UCX.NOTE;1 >>>
                      -< DEC TCP/IP Services for OpenVMS >-
================================================================================
Note 5640.3                 select via QIO interface                      3 of 3
PRSSOS::VOUTERS "Philippe VOUTERS, Evry (France)"   685 lines   7-JUL-1997 02:46
             -< Stars Article after submission and before review >-
--------------------------------------------------------------------------------
    Hello Graham,
    
    Here is the article that has been submitted to STARS and not yet
    technically reviewed. Your feedback about the interest and reada-
    bility of the text below will be appreciated.
    
    Philippe Vouters (MCS France)
    
    
    
     Example-UCX  Example of TCP/IP client/sever $QIO programming using ASTs.



EXAMPLE TEMPLATE:

PRODUCT:    UCX Version V4.1

OP/SYS:     OpenVMS Version 7.1

SOURCE:     Digital Equipment Corporation


OVERVIEW:

            These client/server C sources shows the correct way of pro-
	    gramming using a stream protocol (TCP/IP) with UCX $QIOs.

*** CAUTION ***

These 2 sample programs has been tested using DEC V5.5, UCX V4.1 
on OpenVMS AXP V7.1.  However, we cannot guarantee its effectiveness 
because of the possibility of error in transmitting or implementing it.  
It is meant to be used as a template for writing your own programs,
and may require modification for use on your system. 


PROGRAMS NOTES:

	Compile and link commands:
	$ CC UCX$TCP_CLIENT_QIO
	$ LINK UCX$TCP_CLIENT_QIO
	$ CC UCX$TCP_SERVER_QIO
        $ LINK UCX$TCP_SERVER_QIO

PROGRAMS:

*****			UCX$TCP_CLIENT_QIO.C		******
/*
 * This is the UCX$TCP_CLIENT_QIO.C modified to use a WRITE Asynchronous
 * System Trap (AST) routine. Because TCP is a stream protocol, it is
 * usefull to check the writability of the socket before writing.
 * All programmers using the $QIO interface of UCX, should write an equi-
 * valent routine.
 */




/*			COPYRIGHT (C) 1989, 1994 BY
*		DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
*
* This software is furnished under a license and may be used and  copied
* only  in  accordance  with  the  terms  of  such  license and with the
* inclusion of the above copyright notice.  This software or  any  other
* copies  thereof may not be provided or otherwise made available to any
* other person.  No title to and ownership of  the  software  is  hereby
* transferred.
*
* The information in this software is subject to change  without  notice
* and  should  not  be  construed  as  a commitment by DIGITAL EQUIPMENT
* CORPORATION.
*/

#include <in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <starlet.h>
#include <lib$routines.h>
#include <descrip.h>
#include <ssdef.h>
#include <iodef.h>
#include <ucx$inetdef.h>	/* Provided during UCX installation */

/* Convert short port number from host to network byte order */
#define htons(x) ((unsigned short)((x<<8)|(x>>8)))
    
typedef struct {
	short channel;
	short *iosb;
	char *buf;
	int buflen;
} ast_param_type;
    
int ast (ast_param_type *ast_param){
    int status;

    /* Write I/O buffer. */
    status = sys$qiow(3,			/* Event flag */
		      ast_param->channel,	/* Channel number */
		      IO$_WRITEVBLK,		/* I/O function */
		      ast_param->iosb,		/* I/O status block */
		      0, 0,
		      ast_param->buf,		/* P1 buffer */
		      ast_param->buflen,	/* P2 buffer length */
		      0, 0, 0, 0);
    if (status & 1) status = ast_param->iosb[0];
    if (!(status & 1))
	printf("Failed to write to socket.\n");
    printf ("QIO WRITEVBLK iosb.status = %d\n",ast_param->iosb[0]);
    /*
     * If partial write denoted by the iosb.count (iosb[1])
     * re-issue a write select, otherwise unblocks main by
     * setting the event flag 4.
     */
    if (ast_param->iosb[1] < ast_param->buflen){
	/*
	 * Adjust buffer address and length to be sent (remaining).
	 */
	ast_param->buf += ast_param->iosb[1];
	ast_param->buflen -= ast_param->iosb[1];
	status = sys$qio(3,		/* Event flag */
			ast_param->channel,	/* Channel number */
			IO$_SETMODE | IO$M_WRTATTN,	/* I/O function */
			ast_param->iosb,		/* I/O status block */
			0, 0,
			ast,		/* P1 buffer */
			&ast_param,		/* P2 buffer length */
			0, 0, 0, 0);
	if (!(status & 1))
		printf("Failed to $ QIO select to socket in ast.\n");
	}
    else
	sys$setef(4);
    return (SS$_NORMAL);
}

main() {
	int	status;		/* For return status */
	short	channel;	/* INET channel */
	short	sck_parm[2];	/* Socket creation parameter */
	short	iosb[4];	/* I/O status block */

	struct {short len; char buffer[512];} buf;
	int	buflen;		/* buffer length */
	int	retval;
        int	on = 1;
	char	junk[256], dot, *dummy;	/* used for scanf */
	short	port;
	struct	SOCKADDRIN remote_host;

        struct item_list {
		unsigned short length;
		unsigned short code;
		void *buffer;
	} tcp_nodelay;

	struct item_list socket_item_list;

        struct dsc {
	        unsigned short length;
		unsigned short type;
		void *buffer;
	} tcp_descr,socket_opt; 

	struct	IL2 {
		unsigned int il2_length;
		char *il2_address;
	} rhst_adrs;
        int one = 1;
	struct	dsc$descriptor inet_dev =
		{10, DSC$K_CLASS_S, DSC$K_DTYPE_T, "UCX$DEVICE"};
        ast_param_type ast_param;

	tcp_nodelay.length = sizeof(on);
	tcp_nodelay.code = UCX$C_TCP_NODELAY;
	tcp_nodelay.buffer = &on;
        tcp_descr.length  = sizeof(tcp_nodelay);
        tcp_descr.type = UCX$C_TCPOPT;
	tcp_descr.buffer = &tcp_nodelay;

        rhst_adrs.il2_length  = sizeof remote_host;
        rhst_adrs.il2_address = (char *) &remote_host; 

	sck_parm[0] = UCX$C_TCP;		/* TCP/IP protocol */
	sck_parm[1] = INET_PROTYP$C_STREAM;	/* stream type of socket */

	remote_host.SIN$W_FAMILY = UCX$C_AF_INET;	/* INET family */
	dummy = (char *)&remote_host.SIN$L_ADDR;
	while (retval != 7) {
		printf ("Enter remote host Internet address (a.b.c.d):\n");
		retval = scanf("%d %c %d %c %d %c %d", 
			&dummy[0], &dot, &dummy[1], &dot,
			&dummy[2], &dot, &dummy[3]);
		if (retval != 7) 
			scanf("%s",junk);	/* discard bad input */
	} 

	while (retval != 1) {
		printf("Enter remote port number:\n");
		retval = scanf("%hd", &port);
		if (retval == 1)
			remote_host.SIN$W_PORT = htons(port); 
		else scanf("%s",junk);	/* discard bad input */
	}

	/* Assign a channel to the UCX device. */
	status = sys$assign(&inet_dev, &channel, 0, 0);
	if (!(status & 1)) {
		printf("Failed to assign channel to UCX device.\n");
		exit(status);
	}

	/* Create the socket */
	status = sys$qiow(3,		/* Event flag */
			channel,	/* Channel number */
			IO$_SETMODE,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			&sck_parm, 0,	/* P1 Socket creation parameter */
			0, 0,
			&tcp_descr, 0);
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to create and bind the device socket.\n");
		exit(status);
	}

	socket_item_list.length = sizeof(one);
	socket_item_list.code = UCX$C_FULL_DUPLEX_CLOSE;
	socket_item_list.buffer =  &one;
	socket_opt.length = sizeof(socket_item_list);
	socket_opt.type =  UCX$C_SOCKOPT;
	socket_opt.buffer =  &socket_item_list;
	/* Set the FULL_DUPLEX option. */
	status = sys$qiow(3,		/* Event flag */
			channel,	/* Channel number */
			IO$_SETMODE,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			0, 0,	/* P1 Socket creation parameter */
			0, 0,
			&socket_opt, 0);	/* P5 Socket option descriptor */
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to create and bind the device socket.\n");
		exit(status);
	}

	/* Connect to specified host and port number */
	status = sys$qiow(3,		/* Event flag */
			channel,	/* Channel number */
			IO$_ACCESS,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			0, 0,
			&rhst_adrs,	/* P3 Remote IP address */
			0, 0, 0);
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to connect to remote host.\n");
		exit(status);
	}
        /*
         * Setup the buffer to be sent and perform a select on a "writemask"
	 * with SETMODE | WRTATTN. This will arm an AST in which will be done
         * the actual writing. Clear also the local Event flag which will
	 * indicates write done.
	 */
        ast_param.channel = channel;
	ast_param.iosb = iosb;
	ast_param.buf = (char *)&buf;
        strcpy(buf.buffer,"Hi there");
	buf.len = strlen(buf.buffer);
	ast_param.buflen = sizeof (buf.len) + strlen(buf.buffer);
        status = sys$clref(4);
	status = sys$qio(3,		/* Event flag */
			channel,	/* Channel number */
			IO$_SETMODE | IO$M_WRTATTN,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			ast,		/* P1 buffer */
			&ast_param,		/* P2 buffer length */
			0, 0, 0, 0);
	if (!(status & 1))
		printf("Failed to $ QIO select to socket in main.\n");

        status = sys$waitfr(4);
	/* Shut down the socket (optional). */
	status = sys$qiow(3,
			channel,
			IO$_DEACCESS|IO$M_SHUTDOWN,
			iosb,
			0, 0,
			0, 0, 0,
			UCX$C_DSC_ALL,	/* P4 Discard all packets */
			0, 0);
	if (status & 1) status = iosb[0];
	if (!(status & 1))
		printf("Failed to shut down the socket.\n");

	/* Close the socket (optional). */
	status = sys$qiow(3,
			channel,
			IO$_DEACCESS,
			iosb,
			0, 0,
			0, 0, 0, 0, 0, 0);
	if (status & 1) status = iosb[0];
	if (!(status & 1))
		printf("Failed to close the socket.\n");

	/* Deassign the UCX device channel. */
	status = sys$dassgn(channel);
	if (!(status & 1))
		printf("Failed to deassign the channel.\n");
}

*****			UCX$TCP_SERVER_QIO.C			*****

/*			COPYRIGHT (C) 1989, 1994 BY
*		DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
*
* This software is furnished under a license and may be used and  copied
* only  in  accordance  with  the  terms  of  such  license and with the
* inclusion of the above copyright notice.  This software or  any  other
* copies  thereof may not be provided or otherwise made available to any
* other person.  No title to and ownership of  the  software  is  hereby
* transferred.
*
* The information in this software is subject to change  without  notice
* and  should  not  be  construed  as  a commitment by DIGITAL EQUIPMENT
* CORPORATION.
*/

#include <in.h>
#include <string.h>
#include <starlet.h>
#include <stdio.h>
#include <descrip.h>
#include <iodef.h>
#include <ucx$inetdef.h>	/* Provided during UCX installation */
#include <stdlib.h>
#include <ssdef.h>
#include <lib$routines.h>
    
/* Convert short port number from host to network byte order */
#define htons(x) ((unsigned short)((x<<8)|(x>>8)))

typedef struct {
	short	channel;/* INET channel */
	short	channel_1;/* Template for ACCEPT */
} chan_t;

typedef struct {
	int *flink;
	int (*exit_handler)();
        unsigned int arg;
	int *status;
	chan_t *channels;
}desblk_t;

static int read,accepted;
static chan_t  channels;
typedef struct {
	short channel;
	short *iosb;
	char *buf;
	int buflen;
} ast_param_type;
typedef struct {
	short len;
	char buffer [512];
} buf_type;

int ast (ast_param_type *ast_param){
    int status;
    static int len;
    static buf_type buffer;

    /* Read I/O buffer. */
    status = sys$qiow(3,			/* Event flag */
		      ast_param->channel,	/* Channel number */
		      IO$_READVBLK,		/* I/O function */
		      ast_param->iosb,		/* I/O status block */
		      0, 0,
		      ast_param->buf,		/* P1 buffer */
		      ast_param->buflen,	/* P2 buffer length */
		      0, 0, 0, 0);
    if (status & 1) status = ast_param->iosb[0];
    if (!(status & 1))
	printf("Failed to read from socket.\n");
    printf ("QIO READVBLK iosb.status = %d\n",ast_param->iosb[0]);
    /*
     * If partial read denoted by the len + iosb.count (iosb[1]) lower
     * than expected size, re-issue a write seklect, otherwise unblocks main
     * by setting the event flag 4.
     */
    memcpy (((char *)&buffer)+len,ast_param->buf,ast_param->iosb[1]);
    len += ast_param->iosb[1]; 
    if ((len < sizeof(short)) && (buffer.len > strlen(buffer.buffer))) {
	/*
	 * Adjust buffer address and length to be received (remaining).
	 */
	ast_param->buf += ast_param->iosb[1];
	ast_param->buflen -= ast_param->iosb[1];
	status = sys$qio(3,		/* Event flag */
			ast_param->channel,	/* Channel number */
			IO$_SETMODE | IO$M_READATTN,	/* I/O function */
			ast_param->iosb,		/* I/O status block */
			0, 0,
			ast,			/* P1 ast routine */
			&ast_param,		/* P2 ast paramter */
			0, 0, 0, 0);
	if (!(status & 1))
		printf("Failed to $ QIO select to socket in ast.\n");
    }
    else {
	printf ("Received text: %.*s\n",buffer.len,buffer.buffer);
	sys$setef(4);
    }
    return (SS$_NORMAL);
}



int cleanup (int *stat,chan_t *channels)
{
        int     status;         /* used in RTL calls    */
	short	iosb[4];	/* I/O status block */

  printf ("Exiting the application ...\n");

  /* Shut down the socket (optional). */
  if (accepted) {
         status = sys$qiow(3,
		    channels->channel_1,
		    IO$_DEACCESS|IO$M_SHUTDOWN,
		    iosb,
		    0, 0,
		    0, 0, 0,
		    UCX$C_DSC_ALL,	/* P4 Discard all packets */
		    0, 0);
        if (status & 1) status = iosb[0];
        if (!(status & 1))
              printf("Failed to shut down the socket (accepted)\n");
        /* Close the sockets -- accepted and listner (optional). */
        status = sys$qiow(3,
	        	  channels->channel_1,
		          IO$_DEACCESS|IO$M_NOW,
		          iosb,
		          0, 0,
		          0, 0, 0, 0, 0, 0);
        if (status & 1) status = iosb[0]; 
        if (!(status & 1))
              printf("Failed to close the socket (accepted).\n");
  }
  status = sys$qiow(3,
		    channels->channel,
		    IO$_DEACCESS|IO$M_NOW,
		    iosb,
		    0, 0,
		    0, 0, 0, 0, 0, 0);
  if (status & 1) status = iosb[0];
  if (!(status & 1))
      printf("Failed to close the socket (listener).\n");

  /* Deassign the UCX device channels. */
  status = sys$dassgn(channels->channel_1);
  if (status & 1) status = sys$dassgn(channels->channel);
  if (!(status & 1))
      printf("Failed to deassign the channel.\n");

  return(*stat);
}

main() {
	short	sck_parm[2];	/* Socket creation parameter */
	short	iosb[4];	/* I/O status block */
        unsigned long oldmask,  /* old interrupt mask   */
        	      newmask;  /* new interrupt mask   */
        int     status;         /* used in RTL calls    */
	desblk_t desblk;	
	buf_type	buf;
	int	buflen = 512;	/* buffer length */
	int	retval;
        char	c;
	short	port;
	unsigned char *dummy;
	unsigned int	r_retlen, one=1;
	struct	SOCKADDRIN local_host, remote_host;
        ast_param_type ast_param;
	struct	IL2 {
		unsigned int il2_length;
		void *il2_address;
	} lhst_adrs;

	struct	IL3 {
		unsigned int il3_length;
		void *il3_address;
		unsigned int *il3_retlen;
	} rhst_adrs;

        struct { int l_onoff;int l_linger;} linger_time = {1,10};

	struct	dsc$descriptor inet_dev =
		{10, DSC$K_CLASS_S, DSC$K_DTYPE_T, "UCX$DEVICE"};

	struct	{ short len, param; void *ptr; }
		item_list[2],tcp_item_list,options,tcp_opt;

        rhst_adrs.il3_length = sizeof remote_host;
        rhst_adrs.il3_address = &remote_host;
	rhst_adrs.il3_retlen = &r_retlen;
        lhst_adrs.il2_length = sizeof local_host;
	lhst_adrs.il2_address = &local_host;

        /* Initialize the socket options item list and descriptor */
	item_list[0].len = sizeof(one);
	item_list[0].param = UCX$C_REUSEADDR;
	item_list[0].ptr =  &one;
	item_list[1].len = sizeof(linger_time);
	item_list[1].param = UCX$C_LINGER;
	item_list[1].ptr = &linger_time;
	options.len = sizeof(item_list);
	options.param =  UCX$C_SOCKOPT;
	options.ptr =  item_list;

	tcp_item_list.len = sizeof(one);
	tcp_item_list.param = UCX$C_FULL_DUPLEX_CLOSE;
	tcp_item_list.ptr =  &one;
	tcp_opt.len = sizeof(tcp_item_list);
	tcp_opt.param =  UCX$C_SOCKOPT;
	tcp_opt.ptr =  &tcp_item_list;
        /*
         * Initialize the declare exit handler data block and call $DCLEXH
         */
        desblk.exit_handler = &cleanup;
	desblk.arg = 2;
        desblk.status = &status;
	desblk.channels = &channels;
	status = sys$dclexh(&desblk);
	if (!(status & 1)) {
		printf("Failed to declare exit handler.\n");
		exit(status);
	}

	sck_parm[0] = UCX$C_TCP;		/* TCP/IP protocol */
	sck_parm[1] = INET_PROTYP$C_STREAM;	/* stream type of socket */
	local_host.SIN$W_FAMILY = UCX$C_AF_INET;	/* INET family */
	local_host.SIN$L_ADDR = UCX$C_INADDR_ANY;	/* Any address */
        accepted = 0;

	while (retval != 1) {
		printf("Enter local port number:\n");
		retval = scanf("%hd", &port);
		if (retval == 1) 
			local_host.SIN$W_PORT = htons(port); 
		else scanf("%s",buf);	/* discard bad input */
	}

	/* Assign two channels to the UCX device. */
	status = sys$assign(&inet_dev, &channels.channel, 0, 0);
	if (status & 1)
		status = sys$assign(&inet_dev, &channels.channel_1, 0, 0);
	if (!(status & 1)) {
		printf("Failed to assign channel to UCX device.\n");
		exit(status);
	}

	/* Create the socket and set the REUSEADDR option. */
	status = sys$qiow(3,		/* Event flag */
			channels.channel,	/* Channel number */
			IO$_SETMODE,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			&sck_parm, 0,	/* P1 Socket creation parameter */
			0, 0,
			&options, 0);	/* P5 Socket option descriptor */
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to create the device socket.\n");
		exit(status);
	}
	/* Bind to chosen port number (after REUSEADDR is set above). */
	status = sys$qiow(3,		/* Event flag */
			channels.channel,	/* Channel number */
			IO$_SETMODE,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			0, 0,
			&lhst_adrs,	/* P3 local socket name */
			3,		/* P4 Connection backlog */
			0, 0);
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to bind the device socket.\n");
		exit(status);
	}

	/* Accept a connection from a client. */
	status = sys$qiow(3,		/* Event flag */
			channels.channel,	/* Channel number */
			IO$_ACCESS|IO$M_ACCEPT,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			0, 0,
			&rhst_adrs,	/* P3 Remote IP address*/
			&channels.channel_1,	/* P4 Channel for new socket */
			0, 0);
        accepted++;
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to accept a connection from a client.\n");
		exit(status);
	}
	dummy = (unsigned char *)&remote_host.SIN$L_ADDR;
	printf("Connection from host: %d.%d.%d.%d, port: %d\n",
		dummy[0], dummy[1], dummy[2], dummy[3],
		htons(remote_host.SIN$W_PORT));

	/* Set the TCP option. */
	status = sys$qiow(3,		/* Event flag */
			channels.channel_1,	/* Channel number */
			IO$_SETMODE,	/* I/O function */
			iosb,		/* I/O status block */
			0, 0,
			0, 0,	/* P1 Socket creation parameter */
			0, 0,
			&tcp_opt, 0);	/* P5 Socket option descriptor */
	if (status & 1) status = iosb[0];
	if (!(status & 1)) {
		printf("Failed to set the device socket TCP options.\n");
		exit(status);
	}
        /*
	 * perform a select on a "readmask" with SETMODE | READATTN.
         * This will arm an AST in which will be done the actual reading.
         * Clear also the local Event flag which will indicates read done.
	 */
        ast_param.channel = channels.channel_1;
	ast_param.iosb = iosb;
	ast_param.buf = (char *)&buf;
	ast_param.buflen = sizeof (buf);
        status = sys$clref(4);
	status = sys$qio(3,			/* Event flag */
			channels.channel_1,		/* Channel number */
			IO$_SETMODE | IO$M_READATTN,	/* I/O function */
			iosb,			/* I/O status block */
			0, 0,
			ast,			/* P1 ast routine */
			&ast_param,		/* P2 ast paramter */
			0, 0, 0, 0);
	if (!(status & 1))
		printf("Failed to $ QIO select to socket in ast.\n");
        status = sys$waitfr (4);
        exit(0);
}



REFERENCE(S):

	DEC TCP/IP system services for OpenVMS
	System Services and C Socket Programming.
	Order Number: AA-LU51H-TE

\
\
\ CONTRIBUTORS:
\
\      Technical:
\           Philippe Vouters (161304)
\           {tech_contributor_name} ({badge})
\
\      Editorial:
\           {edit_contributor_name} ({badge})
\           {edit_contributor_name} ({badge})


\! Please do not remove lines below this point - for administration purposes
\! ---------------------------------------------------------------------------
\\ TYPE=TECH_TIPS TYPE=EXAMPLE

