/* ******************************************************************************* ** FILE: BGdriver_TCP_server.C -- example program demonstrating usage of ** network programming library using BGdriver $QIO's ** ** PRODUCT: TCPware for VMS ** ** VERSION: V5.6 ** ** Copyright (c) 2001, 2002 by ** Process Software LLC ** Framingham, Massachusetts ** ** Copyright (c) 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: ** ** BGdriver_TCP_server.C and BGdriver_client.c are a pair of example programs ** illustrating the use of stream sockets via VMS's SYS$QIO system service ** calls to the BGdriver. ** ** ** SERVER SEQUENCE OF OPERATIONS: ** 1. Clone the listen & connect TCP devices $ASSIGN; ** 2. Create socket: $QIO( IO$_SETMODE); ** 3. Bind local port: $QIO( IO$_SETMODE); ** 4. Accept client connection: $QIO( IO$_ACCESS | IO$M_ACCEPT ); ** 3. Exchange data: $QIO(IO$_WRITEVBLK), $QIO(IO$_READVBLK); ** 4. Close connection: $QIO( IO$_DEACCESS); ** 5. Deassign the channel: $DASSGN; ** ** ** BUILDING EXECUTABLES: ** ( /DEFINE=TCPWARE points to TCPware's ucx$inetdef.h instead of UCX's file) ** ** 1. with DECC: ** $ CC/DECC/PREFIX_LIBRARY_ENTRIES=ALL/DEFINE=TCPWARE BGdriver_TCP_server.C ** $ LINK BGdriver_TCP_server ** ** 2. with VAXC: ** $ CC/DEFINE=TCPWARE BGdriver_TCP_server.C ** $ LINK BGdriver_TCP_server, TCPWARE:UCX$IPC/LIB, SYS$INPUT/OPTIONS ** SYS$SHARE:VAXCRTL/SHARE ** ******************************************************************************* ** 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 /* comes after in.h */ #include #ifdef TCPWARE #include "TCPWARE_INCLUDE:ucx$inetdef.h" /* Provided by TCPware install */ #else /* assume UCX */ #include /* Provided during UCX installation */ #endif /* ** 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; }; /* ** item list 2 descriptor used for local host name ** (used in $QIO IO$_SETMODE) */ struct item_list_2_struct { short complen; short itmcode; char *compadr; }; /* ** item list 3 descriptor used for remote host name ** (used in $QIO IO$_READVBLK) */ struct item_list_3_struct { short buflen; short itmcode; char *buffadr; short *retlenadr; }; /* ** socket definition (used in $QIO IO$SETMODE) */ struct sockdef_struct { unsigned short protocol; unsigned char type; unsigned char domain; }; #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; }; main() { unsigned short listen_chan; /* TCP listen channel */ unsigned short channel; /* TCP client connect channel */ int status; /* For return status */ int cont; /* boolean continue status */ int io_OK; /* boolean $qio status */ int i; int backlog = 5; /* listen backlog */ 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; char cont_str[80]; /* setup socket definition for $QIO IO$_SETMODE P1 arg */ static struct sockdef_struct sockdef = { UCX$C_TCP, /* TCP protocol */ INET_PROTYP$C_STREAM, /* stream type of socket */ UCX$C_AF_INET /* Internet domain */ }; /* UCX's SOCKADDRIN is same as in.h's sockaddr_in Socket Address Structure*/ static struct SOCKADDRIN s_adr = { UCX$C_AF_INET, /* internetwork family */ 0, /* SIN$W_PORT is setup below */ UCX$C_INADDR_ANY, /* accept on any IP address */ 0 /* SIN$T_ZERO */ }; /* local port & address used in $QIO IO$_SETMODE P3 arg*/ static struct item_list_2_struct s_adr_item2 = { sizeof( s_adr), 0, (char *) &s_adr }; /* remote port & address used in $QIO (IO$_ACCESS | IO$M_ACCEPT) */ static int remote_adr_len; static struct SOCKADDRIN remote_s_adr; static struct item_list_3_struct remote_s_adr_item3 = /*remote port & addr*/ { sizeof( remote_s_adr),0, (char *)&remote_s_adr, (short *)&remote_adr_len }; /* String descriptor for the UCX device BG0: */ static $DESCRIPTOR( BG0_dev, "UCX$DEVICE"); /* ** Get server port number for named service. ** If unknown ( == 0 or = -1), use default define. */ if ( (int)(servent_P = getservbyname( SERVICE, PROTO)) > 0) s_adr.SIN$W_PORT = servent_P->s_port; else s_adr.SIN$W_PORT = htons(SERVER_PORT); /* Assign a channel to UCX$DEVICE to clone a new device for listen socket */ status = sys$assign( &BG0_dev, &listen_chan, 0, 0); if (FAILURE( status) ) { printf("Failed to assign channel to device BG0.\n"); exit(status); } /* ** This $QIO combines 2 network operations: ** Create socket (specified in P1) - equivalent to BSD socket(). ** Bind to port (specified in P3) - equivalent to BSD bind(). */ status = sys$qiow( 0, listen_chan, IO$_SETMODE, &iosb, 0, 0, &sockdef, /* P1: socket definition */ 0, &s_adr_item2, /* P3: local socket */ 0, 0, 0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Failed to create socket or bind to port\n"); exit( status); } /* ** Do listen operation to have network kernel queue up incoming connections ** to the specified TCP port. - equivalent to BSD socket(). */ status = sys$qiow( 0, listen_chan, IO$_SETMODE, &iosb, 0, 0, 0,0,0, backlog, /* P4: max # of connects to queue */ 0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("Failed to listen on port\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( s_adr.SIN$W_PORT) ); /* ** Assign a new channel to accept the connection. */ status = sys$assign( &BG0_dev, &channel, 0, 0); if (FAILURE( status) ) { printf("Failed to assign channel to device BG0.\n"); exit(status); } /* ** Accept the connection */ status = sys$qiow( 0, listen_chan, (IO$_ACCESS | IO$M_ACCEPT), &iosb, 0, 0, 0,0, &remote_s_adr_item3, /* P3: remote info */ &channel, /* P4: Device channel */ 0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) { printf("IO$CREATE accept failed\n"); exit(status); } /* ** Use remote IP address to query DNS for host name. ** If unknown, just show remote IP address. */ remote_in_addr.s_addr = remote_s_adr.SIN$L_ADDR; 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, &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$_CONNECFAIL) 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. ** ** If client closes prematurely, IO$_READVBLK $QIO gets ** SS$_CONNECFAIL error; IO$_WRITEVBLK gets SS$_LINKDISCON error. */ 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$_CONNECFAIL) 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$_DEACCESS, &iosb, 0,0, 0,0,0,0,0,0); if ( SUCCESS( status) ) status = iosb.status; if ( FAILURE( status) ) 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; } /* Deassign the channel used to listen */ status = sys$dassgn( listen_chan); }