Table 2-4 describes the basic calling sequence for client applications that use the UDP protocol.
Task | C Socket Routine | OpenVMS System Service Routine |
---|---|---|
Create a device socket | socket() |
$ASSIGN
$QIO(IO$_SETMODE)¹ |
Bind socket name | bind() | $QIO(IO$_SETMODE)¹ |
Exchange data |
read()
recv() recvfrom() recvmsg() |
$QIO(IO$_READVBLK) |
write()
send() sendto() sendmsg() |
$QIO(IO$_WRITEVBLK) | |
Shut down the socket (optional) | shutdown() | $QIO(IO$_DEACCESS|IO$M_SHUTDOWN) |
Close and delete the socket | close() |
$QIO(IO$_DEACCESS)
$DASSGN |
For communication to take place between two processes, a communication end point is required. Therefore, an abstraction called a socket was created to serve as an end point that sends and receives messages. Sockets are created with an address family similar to the way that files are created within a file system. When an application program requests that a socket be created, a small integer, known as a descriptor, is returned. The application program uses the descriptor to reference the newly created socket.
To create a device socket, complete the following steps:
The functions of the internet protocols are performed as I/O functions of the internet pseudodevice. When using the DIGITAL TCP/IP Services for OpenVMS software, UCX$DEVICE: is the logical name for internet pseudodevices.
When a channel is assigned to the UCX$DEVICE template internet
pseudodevice, the internet software creates a new pseudodevice with a
unique unit number. The returned channel number is used in subsequent
operation requests with that device.
2.2.1 Creating Device Sockets with OpenVMS System Services
When you use OpenVMS system services, you make two calls to create the device socket:
Example 2-1 shows a TCP/IP server using the $ASSIGN and $QIOW system services to create a device socket.
Example 2-1 Creating a Device Socket Using OpenVMS System Services
/* ** ** Assign a channel to the network device, "UCX$DEVICE:". ** */ sysSrvSts = sys$assign( &devDSC, /* devnam.d */ &IOChannel, /* chan.r */ 0, /* acmode.v | 0 */ 0, /* mbxnam.d | 0 */ 0 /* flags.v | 0 */ ); if(( sysSrvSts & 1 ) != 1 ) /* Validate the system service status. */ { sys$dassgn( IOChannel /* chan.v */ ); errorExit( sysSrvSts, 1 ); } /* ** ** Attempt to create device socket. ** Use the function code of IO$_SETMODE, and passing the socket ** creation parameter to P1. ** */ socketParam.protocol = UCX$C_TCP; socketParam.type = INET_PROTYP$C_STREAM; socketParam.family = AF_INET; sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_SETMODE, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ &socketParam, /* p1.r Socket creation parameter */ 0, 0, 0, 0, 0 /* p2, p3, p4, p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the I/O status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); }
2.2.2 Creating Device Sockets with the C Socket Interface
When using the C Socket interface, create the device socket with one
call to the socket() routine. Example 2-2 shows a TCP/IP
server using the socket() routine to create a device socket.
Example 2-2 Binding a Socket Using C Socket Programming
#include <types.h> #include <in.h> . . . #include <string.h> #define PORTNUM 1234 main() { int r, s, i; struct sockaddr_in lcladdr; lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = INADDR_ANY; lcladdr.sin_port = htons(PORTNUM); s = socket(AF_INET,SOCK_STREAM,0;)Z(1) . . .
The Berkeley Internet Name Domain (BIND) service is a host name and address lookup service for the Internet. If BIND is enabled on your system, you can make a call to the BIND resolver to obtain host addresses.
Typically, you make a call to the BIND resolver either before you bind
a socket or before you make a connection to a socket. You can also use
this function to translate either the local or remote host name to an
address before making a connection.
2.3.1 BIND Lookups with OpenVMS System Services
If BIND is enabled on your system, the IO$_ACPCONTROL function searches the BIND database for the host name if it does not find the name in the local host database. The p1 argument allows you to specify the GETHOSTBYADDR or GETHOSTBYNAME subfunctions to control how the routine searches the database.
Example 2-3 shows an example of a BIND lookup with OpenVMS system service.
Example 2-3 BIND Lookup Using OpenVMS System Services
/* ** ** Bind the socket to a local address. ** */ addrLength = sizeof( localAddr ); memset( &localAddr, 0, sizeof( localAddr )); localAddr.sin_family = AF_INET; localAddr.sin_addr.s_addr = INADDR_ANY; localAddr.sin_port = htons( SERVER_PORT ); /* ** ** Declare and populate a string descriptor, (struct dsc$descriptor_s), ** as defined in <descrip.h>. ** */ localAddrDSC.dsc$a_pointer = (char*)&localAddr; localAddrDSC.dsc$w_length = (long)addrLength; /* ** ** Attempt to Bind the device socket to the local host address. ** Use the function code of IO$_SETMODE, and passing the local ** address address information as a descriptor to P3. ** ** NOTE: The functionality of this call, to bind the socket, and the ** functionality of the sys$qiow call to create the socket, could be ** combined into one call. ** */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_SETMODE, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ 0, 0, /* p1, p2 UNUSED */ &localAddrDSC, /* p3.d Socket bind parameter */ 0, 0, 0 /* p4, p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); }
If the BIND resolver is enabled on your system, you can use either of the following routines to search the BIND database if the host name is not found in the local database:
Example 2-4 shows an example of using the gethostname(), gethostbyname(), and gethostbyaddr() routines to find a local host name and address.
Example 2-4 BIND Lookup Using C Socket Programming
/* This program finds the local hostname, then looks itself up both by name and by address. It looks at only the first address returned. */ #include <netdb.h> #include <errno.h> #include <socket.h> #include <in.h> main() { char hostname[33]; struct in_addr our_address; struct hostent *hostent_ptr; if (gethostname(hostname, sizeof(hostname)))perror("gethostname");(1) printf("Local hostname: %s\n", hostname); if (!(hostent_ptr = gethostbyname(hostname))) perror("gethostbyname");(2) else our_address.s_addr = *(int *)hostent_ptr->h_addr; printf("Found host: %s address: %s\n", hostent_ptr->h_name, inet_ntoa(our_address.s_addr)); if (!(hostent_ptr = gethostbyaddr(&our_address.s_addr, sizeof(our_address.s_addr),(3) AF_INET))) perror("gethostbyaddr"); else printf("Back-translated host: %s\n", hostent_ptr->h_name); }
In this example, the following routines and arguments were used to find a local host name and address:
2.4 Binding a Socket
To bind a socket, specify a local interface address and local port
number for the device socket.
2.4.1 Binding Sockets with OpenVMS System Services
Use the IO$_SETMODE or IO$_SETCHAR function of the $QIO system service to bind a socket.
Note
The process must have a system user identification code (UIC), SYSPRV, BYPASS, or OPER privileges to bind port numbers 1 to 1023.
Example 2-5 shows a TCP/IP server using the IO$_SETMODE function to bind sockets.
Example 2-5 Binding Socket Using OpenVMS System Services
/* ** ** Bind the socket to a local address. ** */ addrLength = sizeof( localAddr ); memset( &localAddr, 0, sizeof( localAddr )); localAddr.sin_family = AF_INET; localAddr.sin_addr.s_addr = INADDR_ANY; localAddr.sin_port = htons( SERVER_PORT ); /* ** ** Declare and populate a string descriptor, (struct dsc$descriptor_s), ** as defined in <descrip.h>. ** */ localAddrDSC.dsc$a_pointer = (char*)&localAddr; localAddrDSC.dsc$w_length = (long)addrLength; /* ** ** Attempt to Bind the device socket to the local host address. ** Use the function code of IO$_SETMODE, and passing the local ** address address information as a descriptor to P3. ** ** NOTE: The functionality of this call, to bind the socket, and the ** functionality of the sys$qiow call to create the socket, could be ** combined into one call. ** */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_SETMODE, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ 0, 0, /* p1, p2 UNUSED */ &localAddrDSC, /* p3.d Socket bind parameter */ 0, 0, 0 /* p4, p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); }
Example 2-6 shows an example of a TCP/IP server using the bind() routine to bind a socket name.
Example 2-6 Binding a Socket Using C Socket Programming
#include <types.h> #include <in.h> #include <socket.h> #include <unixio.h> #include <string.h> #define PORTNUM 1234 . . . main() { int r, s, i; struct sockaddr_in lcladdr; char *message = "Hello, world!\r\n"; lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = INADDR_ANY; lcladdr.sin_port = htons(PORTNUM); . . . i = bind( s, (struct sockaddr *)&lcladdr,(1) sizeof(lcladdr));(2) . . .
In this example, the bind() routine specifies the socket descriptor previously created with a call to the socket() routine, as follows:
Only server programs that use TCP need to set a socket to be a
listener. This allows the program to receive incoming connection
requests. To set a socket to listen for connection requests, specify
the number of incoming requests that can wait to be queued for
processing.
2.5.1 Listening to a Socket with OpenVMS System Services
Example 2-7 shows how to use the IO$_SETMODE function to set the socket to listen for requests.
Example 2-7 Setting the Socket to Listen Using OpenVMS System Services
/* ** ** Attempt to inform the network driver that it should enqueue multiple ** simultaneous incoming connection requests. ** Use the function code of IO$_SETMODE, and passing the desired queue ** length by value to P4. ** */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_SETMODE, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ 0, 0, 0, /* p1, p2, p3 UNUSED */ SERVER_QLENGTH, /* p4.v Queue length */ 0, 0 /* p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); }
Note
Although you can issue separate $QIO calls for socket create, bind, and listen operations, you can also perform these operations with one $QIO call. Note that socket name means the same as socket address, and it represents the association of host address and port information.
Example 2-8 shows a TCP/IP server using the bind() routine to set the socket to listen for requests.
Example 2-8 Setting the Socket to Listen Using C Socket Programming
#include <types.h> #include <in.h> #include <socket.h> #include <unixio.h> #include <string.h> #define PORTNUM 1234 main() { int r, s, i; struct sockaddr_in lcladdr; char *message = "Hello, world!\r\n"; lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = INADDR_ANY; lcladdr.sin_port = htons(PORTNUM); . . . i = listen(s,(1) 1);(2)
In this example of a listen() routine:
You can initiate a connection through a TCP client using either a stream socket or datagram. However, you must follow the guidelines listed below:
To initiate a connection to a remote host, issue the $QIO system service with the IO$_ACCESS function and the p3 argument. The p3 argument of the IO$_ACCESS function is the address of a UCX item_list_2 descriptor that contains the remote socket name.
Example 2-9 shows an example of a TCP/IP client using the IO_$ACCESS function to initiate a connection.
The internet address received by the program identifies the remote host. To map the internet address to the host name, use a $QIO system service with the IO$_ACPCONTROL function (see Chapter 4).
Example 2-9 Initiating a Connection Using OpenVMS System Services
/* ** ** Get the remote server host name from the user and set up the remote ** address structure. ** */ memset( &remoteAddr, 0, sizeof( remoteAddr )); remoteAddr.sin_family = AF_INET; remoteAddr.sin_port = htons( SERVER_PORT_NUMBER ); /* Continue to prompt the user for a valid server host name. */ memset( IOBuff, 0, MaxBuff ); printf( "\n Enter the host name were the server is running : " ); scanf( "%s", IOBuff ); while( (hostentptr = gethostbyname( IOBuff )) == (struct hostent*)NULL ) { printf( " Host %s could not be found.\n", IOBuff ); memset( IOBuff, 0, MaxBuff ); printf( "\n Enter the host name were the server is running : " ); scanf( "%s", IOBuff ); } remoteInAddr.s_addr = *(int*)(hostentptr->h_addr); addrStr = (char*)inet_ntoa( remoteInAddr ); printf( "\n connecting to: %s %s\n", hostentptr->h_name, inet_ntoa( remoteInAddr )); remoteAddr.sin_addr = *((struct in_addr*)hostentptr->h_addr ); remoteAddrDSC.dsc$a_pointer = (char*)&remoteAddr; remoteAddrDSC.dsc$w_length = sizeof( remoteAddr ); /* ** ** Attempt to connect the device socket to the local host address. ** Use the function code of IO$_ACCESS, and passing the remote ** address information as a descriptor to P3. ** ** NOTE: The functionality of this call, to connect the socket, and ** the functionality of the sys$qiow call that creates the socket, ** socket, could be combined into one call. ** */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_ACCESS, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ 0, 0, /* p1, p2 UNUSED */ &remoteAddrDSC, /* p3.d Socket connect parameter */ 0, 0, 0 /* p4, p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); }
To initiate a connection to a remote host, issue the connect() routine. Example 2-10 shows an example of a TCP/IP client using the connect() routine to initiate a connection to a remote server.
Example 2-10 Initiating a Connection Using C Socket Programming
. . . int sock_1; /* socket */ static char message[] = "Hi there."; static struct sockaddr_in sock2_name; /* Address struct for socket2.*/ struct hostent hostentstruct; /* Storage for hostent data. */ struct hostent *hostentptr; /* Pointer to hostent data. */ static char hostname[256]; /* Name of local host. */ int flag; int retval; /* helpful for debugging */ int shut = FALSE; /* flag to cleanup */ . . . /* * Fill in the name & address structure for socket 2. */ sock2_name.sin_family = hostentstruct.h_addrtype; sock2_name.sin_port = htons(atoi(argv[2])); sock2_name.sin_addr = * ((struct in_addr *) hostentstruct.h_addr); /* * Connect socket 1 to sock2_name. */ retval = connect(sock_1, &sock2_name, sizeof (sock2_name)); if (retval) { perror("connect"); cleanup(shut, sock_1); }
Your server program must be able to accept incoming connection requests
from client programs to successfully use TCP.
2.7.1 Accepting a Connection Using OpenVMS System Services
To accept a connection request, use the following procedure:
Note
Specifying the IO$_ACCESS function is mandatory for TCP/IP. The IO$_ACCESS function uses the p4 argument only with the IO$M_ACCEPT modifier.
Example 2-11 shows a TCP server using the IO$_ACCESS function with the IO$M_ACCEPT modifier to accept incoming connection requests.
Example 2-11 Accepting an Incoming Connection Request Using OpenVMS System Services
/* ** ** Assign a new channel, for the new connection, to the network device, ** "UCX$DEVICE:". ** */ sysSrvSts = sys$assign( &devDSC, /* devnam.d */ &IOChanClient, /* chan.r */ 0, /* acmode.v | 0 */ 0, /* mbxnam.d | 0 */ 0 /* flags.v | 0 */ ); if(( sysSrvSts & 1 ) != 1 ) /* Validate the system service status. */ { cleanup( IOChanClient ); cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } remoteAddrInfo.length = (unsigned int)addrLength; remoteAddrInfo.address = (char*)&remoteAddr; remoteAddrInfo.retLenPtr = (unsigned int*)&addrLength; /* ** ** Attempt to complete the first connections on a queue of pending ** connections. ** Use the function codes of IO$_ACCESS ORed with IO$M_ACCEPT, passing the ** address of the remote address information to P3, and the address ** of the new channel to the client to P4. ** ** This queue must have previously been established with a QIO using ** function IO$_SETMODE and a queue length passed to P4. ** */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_ACCESS | IO$M_ACCEPT, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ 0, 0, /* p1, P2: UNUSED */ &remoteAddrInfo, /* p3.r Remote IP address */ &IOChanClient, /* p4.r connection Chan */ 0, 0 /* p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChanClient ); cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } printf( " SUCCEEDED in accepting a connections.\n" ); printf( " The client connection port number is %d\n", htons( remoteAddr.sin_port ));