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

Previous | Contents

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)¹
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

¹The $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, 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:

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, 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:

- $ASSIGN to assign a channel to the internet pseudodevice
- $QIO to create the socket
  When you make this call, use either the IO$_SETCHAR or the IO$_SETMODE I/O function. You generally create, bind, and set up sockets to listen with one $QIO call. The IO$_SETCHAR and IO$_SETMODE functions perform in an identical manner, where internet software is concerned. However, you must have logical I/O permissions 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, "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)
       .
       .
       .

1. This command line binds the device socket with the following arguments:
   - socket() is the address that references the socket in internet format which is the only format that UCX supports.
   - SOCK_STREAM specifies that the socket type is stream.
   - 0 specifies the protocol type as TCP/IP which is the default.

   # 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 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 );
               }

   ## 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:

   - gethostbyaddr() gets a host record from the local host or BIND database when given the host address.
   - gethostbyname() gets a host record from the local host or BIND database when given the host name.

   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:

   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 UCX, AF_INET is the only type supported.

   # 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 );
               }

   ## 2.4.2 Binding Sockets with the C Socket Interface

   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:

   1. lcladdr specifies the address of the sockaddr structure that assigns a name to the socket.
   2. sizeof(lcladdr) specifies the size of the sockaddr structure.

   # 2.5 Making a Socket a Listener (TCP Server)

   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.

   ## 2.5.2 Listening to a Socket with a C Socket Interface

   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:

   1. s is the socket descriptor previously defined by a call to the socket() routine.
   2. 1 specifies that only one pending connection can be queued at any given time. The maximum number of connections allowed is 5.

   # 2.6 Initiating a Connection with a TCP Client

   You can initiate a connection through a TCP client using either a stream socket or datagram. However, you must follow the guidelines listed below:

   - Stream socket (TCP) --- requires you to establish a connection with the server application before transferring data.
   - Datagram (UDP) --- establishing a connection before transferring data is optional.

   ## 2.6.1 Initiating a Connection with OpenVMS System Services

   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 );
               }

   ## 2.6.2 Initiating a Connection with a C Socket Interface

   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);
                       }

   # 2.7 Accepting a Connection with a TCP Server

   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:

   1. Issue an $ASSIGN system service to create a channel for the new connection.
   2. Issue a $QIO system service using the IO$_ACCESS function with the IO$M_ACCEPT modifier.
      The p4 argument specifies the channel for I/O operations on the connection established in Step 1. If p3 specifies a valid output buffer, the remote host returns the remote socket name.

      > 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 ));

   Previous | Next | Contents