/* ******************************************************************************* ** FILE: INETdriver_server.C -- example program demonstrating usage of ** network programming library using INETdriver $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: ** ** INETdriver_server.c and INETdriver_client.c are a pair of example programs ** illustrating the use of stream sockets via VMS's SYS$QIO system service ** calls to the INETdriver. ** ** ** SERVER SEQUENCE OF OPERATIONS: ** 1. Assign listen channel $ASSIGN; ** 2. Create stream socket for listen $QIO( IO$_SOCKET); ** 3. Bind socket to an address $QIO( IO$_BIND); ** 4. Listen for incoming connects $QIO( IO$_LISTEN); ** 5. Assign accept channel $ASSIGN; ** 6. Accept incomming connect $QIO( IO$_ACCEPT); ** 7. Exchange data: $QIO(IO$_SEND), $QIO(IO$_RECEIVE); ** 8. Close connection: $DASSGN; ** ** ** BUILDING EXECUTABLES: ** ** 1. on VAX : ** with VAXC: ** $ CC INETDRIVER_SERVER.C ** $ LINK INETDRIVER_SERVER, TCPWARE:UCX$IPC/LIB, SYS$INPUT/OPTIONS ** SYS$SHARE:VAXCRTL/SHARE ** ** Alternatively TCpware's socket library can be used: ** $ LINK INETDRIVER_SERVER, SYS$INPUT/OPTIONS ** SYS$SHARE:TCPWARE_SOCKLIB_SHR/SHARE ** SYS$SHARE:VAXCRTL/SHARE ** ** with DECC: ** $ CC/DECC/PREFIX_LIBRARY_ENTRIES=ALL INETDRIVER_SERVER.C ** $ LINK INETDRIVER_SERVER ** ** 2. on ALPHA: ** $ CC/PREFIX_LIBRARY_ENTRIES=ALL INETDRIVER_SERVER.C ** $ LINK INETDRIVER_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 #include #include /* includes socket error codes */ #include "tcpware_include:inetiodef.h" /* INETDRIVER I/O definitions */ /* ** 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; }; #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 listen_chan; /* TCP channel to listen on */ unsigned short accept_chan; /* TCP channel to accept on */ unsigned short port; /* TCP port number */ int status; /* For return status */ int cont; /* boolean continue status */ int io_OK; /* boolean $qio status */ int bytes_left; /* count of bytes read */ char buffer[1024]; char *c_P; long int servaddr; struct IOSB_struct iosb; struct net_msg_hdr_struct *net_msg_hdr_P; char cont_str[80]; struct servent *servent_P; struct hostent *hostent_P; struct sockaddr_in s_adr; struct sockaddr_in remote_s_adr; /* String descriptor for the INET0 device */ static $DESCRIPTOR( INET0_dev, "INET0"); /* ** Assign a listen channel to INET0 to clone a new device. ** Note that this step is missing in Multinet's programming interface. */ status = sys$assign( &INET0_dev, &listen_chan, 0, 0); if (FAILURE( status) ) { printf("Failed to assign listen channel to device INET0.\n"); exit(status); } /* ** 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 = htons( SERVER_PORT); /* Create stream socket on which to listen */ status = sys$qiow( 0, listen_chan, IO$_SOCKET, &iosb, 0, 0, AF_INET, /* P1: addressfam */ SOCK_STREAM, /* P2: type */ 0, /* P3: protocol */ 0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("socket() failed\n"); exit( status); } /* ** Bind the socket to an assigned address: ** Setup sockaddr_in argument to describe port this server will listen on. ** INADDR_ANY allows server with multiple IP addresses, to accept on any ** of them. */ s_adr.sin_family = AF_INET; s_adr.sin_port = port; s_adr.sin_addr.s_addr = INADDR_ANY; status = sys$qiow( 0, listen_chan, IO$_BIND, &iosb, 0, 0, &s_adr, /* P1: sockaddr_in address */ sizeof( s_adr), /* P2: length of address buf */ 0, 0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("bind() failed\n"); exit( status); } /* listen for incoming connects and setup listen queue depth */ status = sys$qiow( 0, listen_chan, IO$_LISTEN, &iosb, 0, 0, 5, /* P1: max que of pending connects = 5*/ 0,0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("listen() failed\n"); exit( status); } /* ** 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", ntohs( port)); /* ** Assign an accept channel to INET0 to clone a new device. ** Note that this step is missing in Multinet's programming interface. */ status = sys$assign( &INET0_dev, &accept_chan, 0, 0); if (FAILURE( status) ) { printf("Failed to assign accept channel to device INET0.\n"); exit(status); } /* ** Wait for connect to listening socket and associate accept_chan ** with that connection. */ status = sys$qiow( 0, accept_chan, /* chan for device to remote */ IO$_ACCEPT, &iosb, 0, 0, &remote_s_adr, /* P1: remote address info */ sizeof( remote_s_adr), /* P2: length of info buf */ listen_chan, /* P3: chan for listen device */ 0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("accept() failed\n"); break; } /* ** Use remote IP address to query DNS for host name. ** If unknown, just show remote IP address. */ hostent_P = gethostbyaddr( (char *) &remote_s_adr.sin_addr, 4, AF_INET); if (hostent_P) printf("\nClient connect accepted from %s = %s\n", inet_ntoa( remote_s_adr.sin_addr), hostent_P->h_name); else printf("\nClient connect accepted from [%s]\n", inet_ntoa( remote_s_adr.sin_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, accept_chan, IO$_RECEIVE, &iosb, 0, 0, net_msg_hdr_P, /* P1: buffer address */ sizeof( *net_msg_hdr_P), /* P2: max bytes to be written to buf */ 0, /* P3: flags */ 0, /* P4: from address */ 0,0); if ( SUCCESS( status) ) status = iosb.status; /* ** Check if client closed connection (status = TCPware ** ECONNRESET. v. INETDRIVER SYSTEM Service Call Format). */ if ( FAILURE( status) | iosb.byte_count < sizeof( *net_msg_hdr_P) ) { if (status == ( (ECONNRESET * 8) | 0x8000) ) 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 client closes connection. ** 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, accept_chan, IO$_RECEIVE, &iosb, 0, 0, c_P, /* P1: buffer address */ 10, /* P2: max bytes to be written to buf */ 0, /* P3: flags */ 0, /* P4: from address */ 0,0); if ( SUCCESS( status) ) status = iosb.status; /* ** If status & IOSB OK, print message received in this read. ** Else check if client closed connection (status = TCPware ** ECONNRESET. v. INETDRIVER SYSTEM Service Call Format). */ 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 == ( (ECONNRESET * 8) | 0x8000) ) 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, accept_chan, IO$_SEND, &iosb, 0, 0, c_P, /* P1: buffer address */ bytes_left, /* P2: buf size in bytes */ 0, /* P3: flags */ 0, /* P4: "to" address */ 0, /* P5: tolen */ 0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { io_OK = FALSE; if (status == ( (ECONNRESET * 8) | 0x8000) ) printf("Client closed connection.\n"); else printf("Error sending message.\n"); } } } if (io_OK) printf("Message was echoed back.\n\n"); } /* Close connection. Then the server loops back to accept new connect */ status = sys$dassgn( accept_chan); if ( FAILURE( status) ) { if (status == ( (ECONNRESET * 8) | 0x8000) ) printf("Client closed connection.\n"); else printf("Error reading message.\n"); } printf( "Accept new client (Y/N) ? "); gets( cont_str); if (cont_str[0] == 'Y' || cont_str[0] == 'y') cont = TRUE; else cont = FALSE; } /* Deassign the listen channel */ status = sys$dassgn( listen_chan); }