DIGITAL TCP/IP Services for OpenVMS
System Services and C Socket Programming


Previous Contents Index


Chapter 2
Writing Internet Applications

You can use either the OpenVMS system services or the C Socket interface to write TCP/IP applications that run on your corporate internet. These applications consist of a series of system calls that perform tasks, such as creating a socket. These system calls are direct entry points that the client and server processes use to obtain services from the kernel software. System calls look and behave exactly like other procedural calls, taking arguments and returning one or more results including a status value. These arguments can contain values or pointers to objects in the application program.

This chapter describes the process followed by communicating client and server machines. This process reflects the sequence of system calls within the client and server programs (see Tables 2-1 and 2-4). The chapter also includes C Socket and OpenVMS system services example routines for each step in the communication process.

Note

The OpenVMS and C Socket programming examples are located in the TCPIP$EXAMPLES library.

2.1 The Client/Server Communication Process

The most commonly used paradigm in constructing distributed applications is the client/server model. The requester, known as the client, sends a request to a server and waits for a response. The server is an application-level program that offers a service that can be reached over the network. Servers accept requests that arrive over the network, perform their service, and return the result to the client.

In addition to having a client and a server, the connection also has a mode of communication. This variable can be either connection-oriented or connectionless. When writing internet applications, the developer uses the mode of communication required by the application-level service. Therefore, if the application-level service uses the connection-oriented mode of communication, the developer uses the virtual circuit or the Transmission Control Protocol (TCP) approach. If the application-level service uses the connectionless mode of communication, then the developer uses the datagram or the User Datagram Protocol (UDP) approach.

Figure 2-1 shows the communication process for a TCP client/server internet application.

Figure 2-1 TCP Socket Communication Process


In this figure:

  1. Server creates a listener device socket.
  2. Server and client create a device socket.
  3. Server and client bind the socket name.
  4. Server defines a listener.
  5. Client sends a connection request.
  6. Server accepts the connection request.
  7. Server receives device information from the local host.
  8. Data exchange takes place.
  9. Client and server delete the device socket.
  10. Server deletes the listener socket when the service to the client is terminating.

Table 2-1 identifies the basic calling sequence, the C socket routine, and the equivalent system service routine for server applications that use TCP.

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) 1
Bind socket name bind() $QIO(IO$_SETMODE) 1
Define listener device socket listen() $QIO(IO$_SETMODE) 1
Accept connection request accept() $QIO(IO$_ACCESS)
Exchange data read()
recv()
recvmsg()
$QIO(IO$_READVBLK)
  write()
send()
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


1The $QIO system service calls for creating a socket, binding a socket name, and defining an internet pseudodevice as a listener are listed as three separate calls in this table. You can do all three steps with one IO$_SETMODE call.

Table 2-2 describes the basic calling sequence for client applications that use TCP.

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) 1
Bind socket name bind() $QIO(IO$_SETMODE) 1
Connect to server connect() $QIO(IO$_ACCESS)
Exchange data read()
recv()
recvmsg()
$QIO(IO$_READVBLK)
  write()
send()
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


1The $QIO system service calls for creating a socket and binding a socket name are listed as two separate calls in this table. You can do both steps with one IO$_SETMODE call.

Figure 2-2 shows the communication process for a UDP client/server internet application.

Figure 2-2 UDP Socket Communication Process


In this figure:

  1. Server and client create a device socket.
  2. Server and client bind the socket name.
  3. Data exchange takes place.
  4. Server and client delete the device socket.

Table 2-3 identifies the basic calling sequence, the system service routine, and the equivalent C Socket routine for server applications that use UDP.

Table 2-3 UDP Server Calling Sequence and Related Routines
Task C Socket Routine OpenVMS System Service Routine
Create a device socket socket() $ASSIGN
$QIO(IO$_SETMODE) 1
Bind socket name bind() $QIO(IO$_SETMODE) 1
Specify port for incoming datagrams recvfrom() $QIO(IO$_ACCESS)
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


1The $QIO system service calls for creating a socket and binding a socket name are listed as two separate calls in this table. You can do both steps with one IO$_SETMODE call.

Table 2-4 describes the basic calling sequence for client applications that use the UDP protocol.

Table 2-4 UDP Client Calling Sequence and Related Routines
Task C Socket Routine OpenVMS System Service Routine
Create a device socket socket() $ASSIGN
$QIO(IO$_SETMODE) 1
Bind socket name bind() $QIO(IO$_SETMODE) 1
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


1The $QIO system service calls for creating a socket and binding a socket name are listed as two separate calls in this table. You can do both of these steps with one IO$_SETMODE call.

2.2 Creating a Device Socket

For communication to take place between two processes, communication end points are required for each process. 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 file folders are created within a file system, where each major topic includes folders of information relating to that topic. 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:

  1. Assign a channel to the internet pseudodevice.
  2. Create a socket.

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, TCPIP$DEVICE: is the logical name for internet pseudodevices.

When a channel is assigned to the TCPIP$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:

When you make the $QIO or $QIOW call, use either the IO$_SETMODE or the IO$_SETCHAR I/O function. You generally create, bind, and set up sockets to listen with one $QIO call. The IO$_SETMODE and IO$_SETCHAR functions perform in an identical manner, where internet software is concerned. However, you must have logical I/O permissions (LOG_IO privilege) to successfully use the IO$_SETMODE I/O function.

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, "TCPIP$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, passing the socket 
**  creation parameter to P1. 
** 
*/ 
    socketParam.protocol = TCPIP$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);(1)
   .
   .
   .

  1. This command line binds the device socket with the following arguments:

2.3 Using the Berkeley Internet Name Domain Service

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 names and 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 service 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 a BIND lookup with OpenVMS system services.

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, passing the local 
**  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 ); 
        } 
 
 

2.3.2 BIND Lookups with the C Socket Interface

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:

The host record contains both name and address information.

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 hostname of the system where the application is 
   running, 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:

  1. gethostname() gets the local host name.
    hostname is the address of the buffer that receives the name.
    sizeof(hostname) is the size of the buffer that receives the host name.
  2. gethostbyname() looks for the host name and invokes the BIND resolver.
    hostname points to a string that contains the name of the host being sought.
  3. gethostbyaddr() looks for the host record that has the specified address.
    our_address.s_addr specifies the address of the host sought. It points to a series of bytes in network order, not an ASCII string.
    sizeof(our_address.s_addr) specifies the number of bytes in the address to which the first argument points.
    AF_INET points to the supported address format. For TCP/IP Services, AF_INET is the only type supported.


Previous Next Contents Index