/* ******************************************************************************* ** FILE: TCPdriver_server.C -- example program demonstrating usage of ** network programming library using TCPDRIVER $QIO's ** ** PRODUCT: TCPware for VMS ** ** VERSION: V5.6 ** ** Copyright (c) 2001, 2002 by ** Process Software LLC ** Framingham, Massachusetts ** ** Copyright (c) 1997-1999 by ** Process Software Corporation ** Framingham, Massachusetts ** ** This software is furnished under a license for use on a ** single computer system and may be copied only 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 except for use on such ** system and to one who agrees to these license terms. Title ** to and ownership of the software shall at all times remain ** in Process Software LLC's name. ** ** The information in this document is subject to change ** without notice and should not be construed as a commitment ** by Process Software LLC. Process Software LLC assumes no ** responsibility for any errors that may appear in this document. ** ** ** ABSTRACT: ** ** TCPdriver_server.c and TCPdriver_client.c are a pair of example programs ** illustrating the use of stream sockets via VMS's SYS$QIO system service ** calls to the TCpdriver. ** ** ** SERVER SEQUENCE OF OPERATIONS: ** 1. Assign an I/O channel to TCP0: $ASSIGN; ** 2. Passive open of connection: $QIO( using IO$_SETMODE|IO$M_CTRL| ** IO$M_STARTUP|0x0800); ** 3. Exchange data: $QIO(IO$_WRITEVBLK), $QIO(IO$_READVBLK); ** 4. Close connection: $QIO(IO$_SETMODE|IO$M_CTRL|IO$M_SHUTDOWN); ** 5. Deassign the channel: $DASSGN; ** ** ** If USE_CREATE is defined when the module is compiled ** (CC/DEFINE=USE_CREATE), the IO$_CREATE TCPDRIVER functions are used ** to listen and accept connections with Unix-like socket functionality. ** ** BUILDING EXECUTABLES: ** ** 1. on VAX : ** with VAXC: ** $ CC TCPDRIVER_SERVER.C [/DEFINE=USE_CREATE] ** $ LINK TCPDRIVER_SERVER, TCPWARE:UCX$IPC/LIB, SYS$INPUT/OPTIONS ** SYS$SHARE:VAXCRTL/SHARE ** ** Alternatively TCpware's socket library can be used: ** $ LINK TCPDRIVER_SERVER, SYS$INPUT/OPTIONS ** SYS$SHARE:TCPWARE_SOCKLIB_SHR/SHARE ** SYS$SHARE:VAXCRTL/SHARE ** ** with DECC: ** $ CC/DECC/PREFIX_LIBRARY_ENTRIES=ALL TCPDRIVER_SERVER.C [/DEFINE=USE_CREATE] ** $ LINK TCPDRIVER_SERVER ** ** 2. on ALPHA: ** $ CC/PREFIX_LIBRARY_ENTRIES=ALL TCPDRIVER_SERVER.C [/DEFINE=USE_CREATE] ** $ LINK TCPDRIVER_SERVER ** ******************************************************************************* ** REQUEST: If you have comments, please send them to "support@process.com" ******************************************************************************* */ #include /* Standard C i/o */ #include #include #include /* VAX/VMS System services */ #include /* for STS$M_SUCCESS */ #include /* Descriptors */ #include /* VAX/VMS i/o definitions */ #include #include #include #include /* comes after in.h */ /* ** Macros to check VMS success/failure status */ #define SUCCESS(status) (status & STS$M_SUCCESS) #define FAILURE(status) (!(status & STS$M_SUCCESS)) /* ** Service information */ #define SERVER_PORT 65000 #define SERVICE "test_echo" #define PROTO "tcp" /* ** Values for boolean variables */ #define FALSE 0 #define TRUE 1 #if defined(__DECC) || defined(__DECCXX) #pragma __member_alignment __save #pragma __nomember_alignment #endif /* ** I/O status block */ struct IOSB_struct { short unsigned status; short unsigned byte_count; long dev_data; }; /* ** Extended characteristics buffer structure, i.e. ecb structure. ** See Programmer's Guide Table 3-4. */ struct ecb_struct { short para_id; long para_val; }; #define NR_CONNECT_PARAMS (13 +1) #define PEER_IA_PARAM 2 #if defined(__DECC) || defined(__DECCXX) #pragma __member_alignment __restore #endif /* ** Net header needed since TCP function may not transmit entire message. */ struct net_msg_hdr_struct { long msg_len; long seq_num; }; /* ** function prototypes */ long sys$assign( struct dsc$descriptor_s *, unsigned short *, unsigned long, struct dsc$descriptor_s *); long sys$qiow( ); /* no prototyping due to varying P1 - P6 */ long sys$dassgn( unsigned short); main() { unsigned short channel; /* TCP channel number */ unsigned short port; /* TCP port number */ int status; /* For return status */ int cont; /* boolean continue status */ int io_OK; /* boolean $qio status */ int i; int bytes_left; /* count of bytes read */ char buffer[1024]; struct net_msg_hdr_struct *net_msg_hdr_P; char *c_P; struct in_addr remote_in_addr; struct servent *servent_P; struct hostent *hostent_P; struct IOSB_struct iosb; struct ecb_struct ecb[1]; struct dsc$descriptor ecb_dsc; /* extended char buffer descriptor */ struct ecb_struct sense_ecb[NR_CONNECT_PARAMS]; struct dsc$descriptor sense_ecb_dsc; /* sensemode ext char buf descript */ char cont_str[80]; #ifdef USE_CREATE int backlog = 128; /* listen backlog */ unsigned short listen_chan; /* TCP listen channel */ #endif /* USE_CREATE */ /* String descriptor for the TCP0 device */ static $DESCRIPTOR( TCP0_dev, "_TCP0:"); /* ** Get server port number for named service. ** If unknown ( == 0 or = -1), use default define. */ if ( (int)(servent_P = getservbyname( SERVICE, PROTO)) > 0) port = servent_P->s_port; else port = SERVER_PORT; /* setup extended characteristics buffer and its descriptor */ ecb[0].para_id = 1; /* Local port number */ ecb[0].para_val = port; ecb_dsc.dsc$w_length = sizeof( ecb); ecb_dsc.dsc$b_dtype = 0; ecb_dsc.dsc$b_class = 0; ecb_dsc.dsc$a_pointer = (char *)&ecb; /* setup sensemode extended characteristics buffer descriptor */ sense_ecb_dsc.dsc$w_length = sizeof( sense_ecb); sense_ecb_dsc.dsc$b_dtype = 0; sense_ecb_dsc.dsc$b_class = 0; sense_ecb_dsc.dsc$a_pointer = (char *)&sense_ecb; #ifdef USE_CREATE /* Assign a channel to TCP0 to clone a new device */ status = sys$assign( &TCP0_dev, &listen_chan, 0, 0); if (FAILURE( status) ) { printf("Failed to assign channel to device TCP0.\n"); exit(status); } /* ** Bind to port */ status = sys$qiow( 0, listen_chan, (IO$_SETMODE|IO$M_CTRL), &iosb, 0, 0, 0, /* P1 */ &ecb_dsc, /* P2: extended char buffer */ 0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Failed to bind to port\n"); exit( status); } /* ** Do listen */ status = sys$qiow( 0, listen_chan, IO$_CREATE, &iosb, 0, 0, 0,0,0,backlog,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Failed to listen on port\n"); exit(status); } #endif /* USE_CREATE */ /* ** Keep handing another client connection - synchronously. */ net_msg_hdr_P = (struct net_msg_hdr_struct *)&buffer; for (cont = TRUE; cont; ) { printf("\n\nServer is accepting on port %d and awaits client connects.\n", port); #ifndef USE_CREATE /* Assign a channel to TCP0 to clone a new device */ status = sys$assign( &TCP0_dev, &channel, 0, 0); if (FAILURE( status) ) { printf("Failed to assign channel to device TCP0.\n"); exit(status); } /* ** Passive open by server (combines bind(), listen() & accept() ) ** Setup extended characteristics buffer (see documentation on ** IO$SETMODE | IO$M_CTRL). ** Issue $QIO open connection (see documentation on ** IO$SETMODE | IO$M_CTRL | IO$M_STARTUP | ^X0800). */ status = sys$qiow( 0, channel, (IO$_SETMODE|IO$M_CTRL|IO$M_STARTUP|0x0800), &iosb, 0, 0, 0, /* P1 */ &ecb_dsc, /* P2: descrip of extended char buffer*/ 0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Passive open failed\n"); exit(status); } #else /* USE_CREATE */ /* ** Wait for a connection. */ status = sys$qiow( 0, listen_chan, IO$_CREATE, &iosb, 0, 0, 0,0,0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Failed on waiting for connection\n"); exit(status); } /* ** Assign a new channel to accept the connection. */ status = sys$assign( &TCP0_dev, &channel, 0, 0); if (FAILURE( status) ) { printf("Failed to assign channel to device TCP0.\n"); exit(status); } /* ** Accept the connection */ status = sys$qiow( 0, listen_chan, (IO$_CREATE | IO$M_NOW), &iosb, 0, 0, 0,0,channel,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("IO$CREATE accept failed\n"); exit(status); } #endif /* USE_CREATE */ /* ** Get connection characteristics. ** Scan this data to get remote IP address. */ memset( &sense_ecb, 0, sizeof( sense_ecb) ); status = sys$qiow( 0, channel, IO$_SENSEMODE | IO$M_CTRL, &iosb, 0, 0, 0, &sense_ecb_dsc, /* P2: descrip of sense_ecb */ 0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("IO$_SENSEMODE failed\n"); exit(status); } for ( remote_in_addr.s_addr = 0, i = 0; i < NR_CONNECT_PARAMS; i++) { if ( PEER_IA_PARAM == sense_ecb[ i].para_id) { remote_in_addr.s_addr = sense_ecb[ i].para_val; break; } } /* ** Use remote IP address to query DNS for host name. ** If unknown, just show remote IP address. */ hostent_P = gethostbyaddr( (char *) &remote_in_addr.s_addr, 4, AF_INET); if (hostent_P) printf("Client connect accepted from %s = %s\n", inet_ntoa( remote_in_addr), hostent_P->h_name); else printf(" Client connect accepted from [%s]\n", inet_ntoa( remote_in_addr) ); printf("Reading 10 bytes at a time to demonstrate the byte stream property\n"); /* ** Keep receiving and echoing messages till client closes connection or ** IO error. */ for (io_OK = TRUE; io_OK; ) { /* ** Receive message: ** Read to find total message length. */ status = sys$qiow( 0, channel, (IO$_READVBLK | IO$M_PACKED), &iosb, 0, 0, net_msg_hdr_P, /* P1: buffer address */ sizeof( *net_msg_hdr_P), /* P2: max bytes to be written to buf */ 0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) | iosb.byte_count < sizeof( *net_msg_hdr_P) ) { if (status == SS$_VCCLOSED) printf("Client closed connection.\n"); else printf("Error reading net_msg_hdr. IOSB byte_count = %d\n", iosb.byte_count); break; } /* ** Receive message: ** Keep reading till entire message read or IO error. ** Read size is set to 10 to demonstrate the byte stream property ** of the TCP connection. */ for (io_OK = TRUE, c_P = &buffer[0] + sizeof( *net_msg_hdr_P), bytes_left = net_msg_hdr_P->msg_len; io_OK == TRUE && bytes_left > 0; bytes_left -= iosb.byte_count, c_P += iosb.byte_count) { status = sys$qiow( 0, channel, IO$_READVBLK, &iosb, 0, 0, c_P, /* P1: buffer address */ 10, /* P2: max bytes to be read into buf */ 0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; /* If status & IOSB OK, print message received in this read */ if ( SUCCESS( status) ) { /* terminate for printf() */ *(c_P + iosb.byte_count) = '\0'; printf("Read %02d characters: %s\n", iosb.byte_count, c_P); } else { io_OK = FALSE; if (status == SS$_VCCLOSED) printf("Client closed connection.\n"); else printf("Error reading message.\n"); break; } } /* ** If entire message received, then send it back: ** Keep writing till full message is sent, including header. */ if ( io_OK && 0 == bytes_left) { for (c_P = &buffer[0], bytes_left = net_msg_hdr_P->msg_len + sizeof(* net_msg_hdr_P); io_OK == TRUE && bytes_left > 0; bytes_left -= iosb.byte_count, c_P += iosb.byte_count) { status = sys$qiow( 0, channel, IO$_WRITEVBLK, &iosb, 0, 0, c_P, /* P1: buffer address */ bytes_left, /* P2: buf size in bytes */ 0,0,0,0); /* P6: optional timeout */ if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Error sending message.\n\n"); io_OK = FALSE; } } } if (io_OK) printf("Message was echoed back.\n\n"); } /* Close connection. Then the server loops back to accept new connect */ status = sys$qiow( 0, channel, (IO$_SETMODE|IO$M_CTRL|IO$M_SHUTDOWN), &iosb, 0,0, 0,0,0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { if (status == SS$_VCCLOSED) printf("Client closed connection.\n"); else printf("Error closing connection.\n"); } sys$dassgn(channel); channel = 0; /* check if server should accept new client */ printf( "Accept new client (Y/N) ? "); gets( cont_str); if (cont_str[0] == 'Y' || cont_str[0] == 'y') cont = TRUE; else cont = FALSE; } #ifdef USE_CREATE /* Deassign the channel used to listen */ status = sys$dassgn( listen_chan); #endif /* USE_CREATE */ }