OpenVMS Source Code Demos

TCPIP$TCP_CLIENT_QIO

#pragma	module	tcpip$tcp_client_qio			\
		"V100.2"
//============================================================================================
// title      : tcpip$tcp_client_qio_100.c
// author     : Neil Rieck ( https://neilrieck.net )
//		(c) copyright 1999,2014  by Neil Rieck
//            : Waterloo, Ontario, Canada.
// created    : 2014-07-31
// notes      : 1. This program began as a cleanup of "TCPIP$EXAMPLES:tcpip$tcp_client_qio.c"
//		   in 1999 but numerous bug fixes and additional functionality make it my own.
//		2. stack programming on VMS and OpenVMS can be done by "Sockets API" (easier)
//		   or "VMS System Services" (harder; a lot like building an Interociter)
//		3. this program employs sys$qiow (qio wait = synchronous)
//		4. sys$qio (asynchronous) provides even more control (demo coming soon)
// OS         : OpenVMS (Alpha or Itanium) or VMS on VAX
// Stack      : TCP/IP Services V5.0 or higher (but should work with any stack)
// References : HP TCP/IP Services for OpenVMS
//              Sockets API and System Services Programming (manual: BA548-90002)
// compile    : $ cc/prefix=all	tcpip$tcp_client_qio_100.c
// link       : $ link          tcpip$tcp_client_qio_100
// history    :
// ver who when   what
// --- --- ------ ----------------------------------------------------------------------------
// 100 NSR 140731 1. original effort (prior to move from Alpha to Itanium)
//     NSR 140801 2. changed port to 80 then added code to retrieve a webpage.
//============================================================================================
//
//	include the usual stuff
//
#include <descrip.h>				// define OpenVMS descriptors
#include <efndef.h>				// define 'EFN$C_ENF' event flag
#include <in.h>					// define internet related constants,
#include <inet.h>				// define network address info
#include <iodef.h>				// define i/o function codes
#include <netdb.h>				// define network database library info
//
#include <ssdef.h>				// define system service status codes
#include <starlet.h>				// define system service calls
#include <stdio.h>				// define standard i/o functions
#include <stdlib.h>				// define standard library functions

#include <string.h>				// define string handling functions
#include <stsdef.h>				// define condition value fields
//
#include <tcpip$inetdef.h>			// define tcp/ip network constants, structures, and functions
//
//	named constants
//
#define	TCPBUFSIZ	8192			// buffer size
#define	USRBUFSIZ	512			// user input buffer size
#define	SERV_PORTNUM	80			// server port number (80=http)
//
//	structure definitions
//
struct iosb {					// i/o status block
    unsigned short status;			// i/o completion status
    unsigned short bytcnt;			// bytes transferred if read/write
    void *details;				// address of buffer or parameter
};

struct itemlst_2 {				// item-list 2 descriptor/element
    unsigned short length;			// length
    unsigned short type;			// parameter type
    void *address;				// address of item list
};

struct sockchar {				// socket characteristics buffer
    unsigned short prot;			// protocol
    unsigned char type;				// type
    unsigned char af;				// address format
};

//
//	forward references
//
void get_serv_addr( void * );			// get server host address

//================================================================================
//	<<< client main >>>
//
//    This example implements a typical TCP IPv4 client using VMS system services:
//
//	1) create a socket:
//
//	   	sys$assign() and sys$qiow(IO$_SETMODE)
//
//	2) initiate a connection:
//
//	   	sys$qiow(IO$_ACCESS)
//
//	3) transfer data:
//
//	   	sys$qiow(IO$_WRITEVBLK)
//	   	sys$qiow(IO$_READVBLK)
//
//	4) shutdown a socket:
//
//		sys$qiow(IO$_DEACCESS|IO$M_SHUTDOWN)
//
//	5) close and delete a socket:
//
//		sys$qiow(IO$_DEACCESS) and sys$dassgn()
//
//===============================================================================

int main( void ) {

    struct iosb		iosb;				// i/o status block
    unsigned int	status;				// system service return status
    char		buf[TCPBUFSIZ];			// client data buffer
    long		buflen = sizeof( buf );		// length of client data buffer
    long		bytes_w;			//
    long		bytes_r;			//
    long		bytes_r_total;			//
    long		read_count;			//
    unsigned short	conn_channel;			// connect inet device i/o channel
    struct sockchar	conn_sockchar;			// connect socket char buffer
    struct sockaddr_in	serv_addr;			// server socket address structure
    struct itemlst_2	serv_itemlst;			// server item-list 2 descriptor
    $DESCRIPTOR( inet_device, "TCPIP$DEVICE:" );	// string descriptor with logical
							// name of internet pseudodevice
    //
    //	init connection socket characteristics buffer
    //
    conn_sockchar.prot = TCPIP$C_TCP;			// protocol: TCP
    conn_sockchar.type = TCPIP$C_STREAM;		//
    conn_sockchar.af   = TCPIP$C_AF_INET;		// IPv4

    //
    //	init server's item-list descriptor
    //
    memset( &serv_itemlst, 0, sizeof(serv_itemlst) );	// initialize
    serv_itemlst.length  = sizeof( serv_addr );		//
    serv_itemlst.address = &serv_addr;			//

    //
    //	init server's socket address structure
    //
    printf("\n%s%d\n", "-i-this program will connect to a website on port ",SERV_PORTNUM);
    memset( &serv_addr, 0, sizeof(serv_addr) );		// initizalize
    serv_addr.sin_family = TCPIP$C_AF_INET;		// IPv4
    serv_addr.sin_port	 = htons( SERV_PORTNUM );	//
    get_serv_addr( &serv_addr.sin_addr );		// prompt user for address (xlate dns -> ip4)

    //
    //	assign device socket
    //
    printf("-i-creating socket (assign)\n");		// tell the user what's going on
    status = sys$assign( &inet_device,	    		// device name
			 &conn_channel,	    		// i/o channel
			 0,		    		// access mode
			 0		    		// not used
    );

    if ( !(status & STS$M_SUCCESS) ) {
	printf( "-e-Failed to assign i/o channel to TCPIP device\n" );
	exit( status );
    }

    //
    //	create connection socket
    //
    printf("-i-creating socket (qiow)\n");		// tell the user what's going on
    status = sys$qiow(	EFN$C_ENF,	    		// event flag
			conn_channel,	    		// i/o channel
			IO$_SETMODE,	    		// i/o function code
			&iosb,		    		// i/o status block
			0,		    		// ast service routine
			0,		    		// ast parameter
			&conn_sockchar,	    		// p1 - socket char buffer
			0,		    		// p2
			0,		    		// p3
			0,		    		// p4
			0,		    		// p5
			0		    		// p6
			);
    //
    if ( status & STS$M_SUCCESS )			// if the qio was sucessful
	status = iosb.status;				// then we would rather test the operational result
    //
    if ( !(status & STS$M_SUCCESS) ) {
	printf( "-e-Failed to create socket\n" );
	exit( status );
    }

    //
    //	connect to specified host and port number
    //
    printf( "-i-connecting to host: %s, port: %d\n",
	    inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
    //
    status = sys$qiow(	EFN$C_ENF,	    		// event flag
			conn_channel,	    		// i/o channel
			IO$_ACCESS,	    		// i/o function code
			&iosb,		    		// i/o status block
			0,		    		// ast service routine
			0,		    		// ast parameter
			0,		    		// p1
			0,		    		// p2
			&serv_itemlst,	    		// p3 - remote socket name
			0,		    		// p4
			0,		    		// p5
			0		    		// p6
			);

    if ( status & STS$M_SUCCESS )			// if the qio was sucessful
	status = iosb.status;				// then we would rather test the operational result

    if ( !(status & STS$M_SUCCESS) ) {
	printf( "-e-Failed to connect to server\n" );
	exit( status );
    }
    //
    // connection established with a server;
    // now attempt to write on this connection
    //
    sprintf(buf,"%s","GET / HTTP/1.0\n\n");		// we want to retrieve a web page
    bytes_w = strlen(buf);				//
    printf("-i-Sending (qiow): %s\n", buf );		// tell the user what's going on
    status = sys$qiow(	EFN$C_ENF,			// event flag
			conn_channel,			// i/o channel
			IO$_WRITEVBLK,			// i/o function code
			&iosb,				// i/o status block
			0,				// ast service routine
			0,				// ast parameter
			buf,				// p1 - buffer address
			bytes_w,			// p2 - buffer length
			0,				// p3
			0,				// p4
			0,				// p5
			0				// p6
			);
    if ( status & STS$M_SUCCESS )			// if the qio was sucessful
	status = iosb.status;				// then we would rather test the operational result
    //
    if ( !(status & STS$M_SUCCESS) ) {
	printf( "-e-Failed to write data to server connection\n" );
	exit( status );
    }

    //
    // connection established with a server;
    // now attempt to read on this connection
    //
    read_count = 0;						//
    bytes_r_total = 0;						//
    while (TRUE) {						//
	read_count++;						//
	printf("-i-Receive (qiow) Count %d\n",read_count);	// tell the user what's going on
	status = sys$qiow(	EFN$C_ENF,			// event flag
				conn_channel,			// i/o channel
				IO$_READVBLK,			// i/o function code
				&iosb,				// i/o status block
				0,				// ast service routine
				0,				// ast parameter
				buf,		   		// p1 - buffer address
				TCPBUFSIZ,			// p2 - buffer size
				0,				// p3
				0,				// p4
				0,				// p5
				0				// p6
				);

	if ( status & STS$M_SUCCESS )				// if we queue okay
	    status = iosb.status;				// then we would rather test the operational result
	//
	if ( !(status & STS$M_SUCCESS) ) {
	    printf( "-w-Failed to read data from server connection\n" );
	//  exit( status );					// exit program (rather extreme)
	    break;						// exit while
	}else{
	    bytes_r = iosb.bytcnt;				// fetch number of bytes just read
	    buf[bytes_r] = '\0';				// quick hack to ensure the print buf works properly
	    printf("-------------------------\n");		//
	    printf("-i-Received:\n%s\n", buf );			// output client's data buffer
	    bytes_r_total =+ bytes_r;				//
	}
    }
    printf("-i-Total Received Bytes: %ld\n", bytes_r_total );

    //
    //	shutdown connection socket
    //
    printf("-i-shutdown connection socket\n");
    status = sys$qiow( EFN$C_ENF,	    		// event flag
			conn_channel,	    		// i/o channel
			IO$_DEACCESS|IO$M_SHUTDOWN,	// i/o function code
			&iosb,		    		// i/o status block
			0,		    		// ast service routine
			0,		    		// ast parameter
			0,		    		// p1
			0,		    		// p2
			0,		    		// p3
			TCPIP$C_DSC_ALL,   		// p4 - discard all packets
			0,		    		// p5
			0		    		// p6
			);

    if ( status & STS$M_SUCCESS )
	status = iosb.status;

    if ( !(status & STS$M_SUCCESS) ) {
	printf( "Failed to shutdown server connection\n" );
	exit( status );
    }

    //
    // close connection socket
    //
    printf("-i-close connection socket\n");
    status = sys$qiow( EFN$C_ENF,	    // event flag
			conn_channel,	    // i/o channel
			IO$_DEACCESS,	    // i/o function code
			&iosb,		    // i/o status block
			0,		    // ast service routine
			0,		    // ast parameter
			0,		    // p1
			0,		    // p2
			0,		    // p3
			0,		    // p4
			0,		    // p5
			0		    // p6
		     );

    if ( status & STS$M_SUCCESS )
	status = iosb.status;

    if ( !(status & STS$M_SUCCESS) ) {
	printf( "Failed to close socket\n" );
	exit( status );
    }

    //
    // deassign device socket
    //
    printf("-i-deassign device socket\n");
    status = sys$dassgn( conn_channel );

    if ( !(status & STS$M_SUCCESS) ) {
	printf( "Failed to deassign i/o channel to TCPIP device\n" );
	exit( status );
    }

    exit( EXIT_SUCCESS );
}
//==================================================================================
// Get Server Host Address
//
//    This function gets the server host's address from the user and then
//    stores it in the server's socket address structure.  Note that  the
//    user can specify a server host by using either an IPv4  address  in
//    dotted-decimal notation (e.g. 16.20.10.126) or a host  domain  name
//    (e.g. serverhost.hp.com).
//
//    Enter "ctrl/z" to terminate program execution.
//==================================================================================

void get_serv_addr( void *addrptr ) {
    char buf[USRBUFSIZ];
    struct in_addr val;
    struct hostent *host;

    while ( TRUE ) {
	printf( "-?-Enter remote host: " );

	if ( fgets(buf, sizeof(buf), stdin) == NULL ) {
	    printf( "-e-Failed to read user input\n" );
	    exit( EXIT_FAILURE );				// kill whole program
	}

	buf[strlen(buf)-1] = 0;

	val.s_addr = inet_addr( buf );

	if ( val.s_addr != INADDR_NONE ) {
	    memcpy( addrptr, &val, sizeof(struct in_addr) );
	    break;
	}

	if ( (host = gethostbyname(buf)) ) {
	    memcpy( addrptr, host->h_addr, sizeof(struct in_addr) );
	    break;
	}
    }
}
/* -------------------------------------------------------------------------------------

DIGITAL TCP/IP Services for OpenVMS System Services and C Socket Programming
Table 2-2 TCP Client Calling Sequence and Related Routines
Task				C Socket Routine	OpenVMS System Service Routine
-------------------------------	-----------------------	--------------------------------
Create a device socket 		socket()		$ASSIGN
							$QIO(IO$_SETMODE)
Bind socket name		bind()			$QIO(IO$_SETMODE)
Connect to server		connect()		$QIO(IO$_ACCESS)
Exchange data			read()			$QIO(IO$_READVBLK)
				recv()			''
				recvmsg()		''
				write()			$QIO(IO$_WRITEVBLK)
				send()			''
				sendmsg()		''
Shut down the socket (optional) shutdown()		$QIO(IO$_DEACCESS|IO$M_SHUTDOWN)
Close and delete the socket 	close()			$QIO(IO$_DEACCESS)
							$DASSGN
========================================================================================
DIGITAL TCP/IP Services for OpenVMS System Services and C Socket Programming
Table 2-1 TCP Server Calling Sequence and Related Routines
Task				C Socket Routine	OpenVMS System Service Routine
-------------------------------	-----------------------	--------------------------------
Create a device socket		socket()		$ASSIGN
							$QIO(IO$_SETMODE)
Bind socket name		bind()			$QIO(IO$_SETMODE)
Define listener device socket 	listen()		$QIO(IO$_SETMODE)
Accept connection request 	accept()		$QIO(IO$_ACCESS)
Exchange data			read()			$QIO(IO$_READVBLK)
				recv()			''
				recvmsg()		''
				write()			$QIO(IO$_WRITEVBLK)
				send()			''
				sendmsg()		''
Shut down the socket (optional)	shutdown()		$QIO(IO$_DEACCESS|IO$M_SHUTDOWN)
Close and delete the socket	close()			$QIO(IO$_DEACCESS)
							$DASSGN
========================================================================================

------------------------------------------------------------------------------------- */