# Compaq TCP/IP Services for OpenVMS
Sockets API and System Services Programming

Previous | Contents | Index

# 2.9 Reading Data

TCP/IP Services allows the application to read data after it performs the following operations:

- Create a socket
- Bind a socket name to the socket
- Establish a connection

## 2.9.1 Reading Data (Sockets API)

Example 2-15 shows a TCP client using the `recv()` function to read data.

Example 2-15 Reading Data (Sockets API)

    #include <in.h>                     /* define internet related constants,   */
                                        /* functions, and structures            */
    #include <inet.h>                   /* define network address info          */
    #include <netdb.h>                  /* define network database library info */
    #include <socket.h>                 /* define BSD socket api                */
    #include <stdio.h>                  /* define standard i/o functions        */
    #include <stdlib.h>                 /* define standard library functions    */
    #include <string.h>                 /* define string handling functions     */

    #define BUFSZ       1024                /* user input buffer size           */
    #define PORTNUM     12345               /* server port number               */

    void get_servaddr( void *addrptr )
    {
        char buf[BUFSIZ];
        struct in_addr val;
        struct hostent *host;

        while ( TRUE )
            {
            printf( "Enter remote host: " );

            if ( fgets(buf, sizeof(buf), stdin) == NULL )
                {
                printf( "Failed to read user input\n" );
                exit( EXIT_FAILURE );
                }

            buf[strlen(buf)-1] = 0;

            val.s_addr = inet_addr( buf );

            if ( val.s_addr != INADDR_NONE )
                {
                memcpy( addrptr, &val, sizeof(struct in_addr) );
                break;
                }

            if ( (host = gethostbyname(buf)) )
                {
                memcpy( addrptr, host->h_addr, sizeof(struct in_addr) );
                break;
                }
            }
    }

    int main( void )
    {
        char buf[512];
        int nbytes, sockfd;
        struct sockaddr_in addr;

        /*
         * initialize socket address structure
         */

        memset( &addr, 0, sizeof(addr) );
        addr.sin_family = AF_INET;
        addr.sin_port   = htons( PORTNUM );
        get_servaddr( &addr.sin_addr );
        /*
         * create a socket
         */

        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
            {
            perror( "Failed to create socket" );
            exit( EXIT_FAILURE );
            }

        /*
         * connect to specified host and port number
         */

        printf( "Initiated connection to host: %s, port: %d\n",
                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)
              );

        if ( connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0 )
            {
            perror( "Failed to connect to server" );
            exit( EXIT_FAILURE );
            }

        /*
         * read data from connection
         */

        nbytes = recv( sockfd,(1)
     buf,(2)
     sizeof(buf),(3)
     0 (4)
     );
        if ( nbytes < 0 )
            {
            perror( "Failed to read data from connection" );
            exit( EXIT_FAILURE );
            }

        buf[nbytes] = 0;
        printf( "Data received: %s\n", buf );

        exit( EXIT_SUCCESS );
    }

1. `sockfd` is the socket descriptor previously defined by a call to the `connect()` function.
2. `buf` points to the receive buffer where the data is placed.
3. `sizeof (buf)` is the size of the receive buffer.
4. `0` indicates that out-of-band data is not being received.

## 2.9.2 Reading Data (System Services)

The $QIO IO$_READVBLK function transfers data received from the internet host (and kept in system dynamic memory) into the address space of the user's process. After the read operation completes, the data in dynamic memory is discarded.

Example 2-16 shows a TCP client using the IO$_READVBLK function to read data into a single I/O buffer.

Example 2-16 Reading Data (System Services)

    #include <descrip.h>                /* define OpenVMS descriptors           */
    #include <efndef.h>                 /* define 'EFN$C_ENF' event flag        */
    #include <in.h>                     /* define internet related constants,   */
                                        /* functions, and structures            */
    #include <inet.h>                   /* define network address info          */
    #include <iodef.h>                  /* define i/o function codes            */
    #include <netdb.h>                  /* define network database library info */
    #include <ssdef.h>                  /* define system service status codes   */
    #include <starlet.h>                /* define system service calls          */
    #include <stdio.h>                  /* define standard i/o functions        */
    #include <stdlib.h>                 /* define standard library functions    */
    #include <string.h>                 /* define string handling functions     */
    #include <stsdef.h>                 /* define condition value fields        */
    #include <tcpip$inetdef.h>          /* define tcp/ip network constants,     */
                                        /* structures, and functions            */

    #define BUFSZ       1024                /* user input buffer size           */
    #define PORTNUM     12345               /* server port number               */

    struct iosb
        {                                   /* i/o status block                 */
        unsigned short status;              /* i/o completion status            */
        unsigned short bytcnt;              /* bytes transferred if read/write  */
        void *details;                      /* address of buffer or parameter   */
        };

    struct itemlst_2
        {                                   /* item-list 2 descriptor/element   */
        unsigned short length;              /* length                           */
        unsigned short type;                /* parameter type                   */
        void *address;                      /* address of item list             */
        };

    struct sockchar
        {                                   /* socket characteristics           */
        unsigned short prot;                /* protocol                         */
        unsigned char type;                 /* type                             */
        unsigned char af;                   /* address format                   */
        };

    void get_servaddr( void *addrptr )
    {
        char buf[BUFSIZ];
        struct in_addr val;
        struct hostent *host;

        while ( TRUE )
            {
            printf( "Enter remote host: " );

            if ( fgets(buf, sizeof(buf), stdin) == NULL )
                {
                printf( "Failed to read user input\n" );
                exit( EXIT_FAILURE );
                }

            buf[strlen(buf)-1] = 0;

            val.s_addr = inet_addr( buf );

            if ( val.s_addr != INADDR_NONE )
                {
                memcpy( addrptr, &val, sizeof(struct in_addr) );
                break;
                }
            if ( (host = gethostbyname(buf)) )
                {
                memcpy( addrptr, host->h_addr, sizeof(struct in_addr) );
                break;
                }
            }
    }

    int main( void )
    {
        char buf[512];                      /* data buffer                      */
        int buflen = sizeof( buf );         /* length of data buffer            */

        struct iosb iosb;                   /* i/o status block                 */
        unsigned int status;                /* system service return status     */
        unsigned short channel;             /* network device i/o channel       */
        struct sockchar sockchar;           /* socket characteristics buffer    */
        struct sockaddr_in addr;            /* socket address structure         */
        struct itemlst_2 addr_itemlst;      /* socket address item-list         */
        $DESCRIPTOR( inet_device,           /* string descriptor with logical   */
                     "TCPIP$DEVICE:" );     /* name of network pseudodevice     */

        /*
         * initialize socket characteristics
         */

        sockchar.prot = TCPIP$C_TCP;
        sockchar.type = TCPIP$C_STREAM;
        sockchar.af   = TCPIP$C_AF_INET;

        /*
         * initialize socket address item-list descriptor
         */

        addr_itemlst.length  = sizeof( addr );
        addr_itemlst.type    = TCPIP$C_SOCK_NAME;
        addr_itemlst.address = &addr;

        /*
         * initialize socket address structure
         */

        memset( &addr, 0, sizeof(addr) );
        addr.sin_family = TCPIP$C_AF_INET;
        addr.sin_port   = htons( PORTNUM );
        get_servaddr( &addr.sin_addr );

        /*
         * assign i/o channel to network device
         */
        status = sys$assign( &inet_device,      /* device name                  */
                             &channel,          /* i/o channel                  */
                             0,                 /* access mode                  */
                             0                  /* not used                     */
                           );

        if ( !(status & STS$M_SUCCESS) )
            {
            printf( "Failed to assign i/o channel\n" );
            exit( status );
            }

        /*
         * create a socket
         */

        status = sys$qiow( EFN$C_ENF,           /* event flag                   */
                           channel,             /* i/o channel                  */
                           IO$_SETMODE,         /* i/o function code            */
                           &iosb,               /* i/o status block             */
                           0,                   /* ast service routine          */
                           0,                   /* ast parameter                */
                           &sockchar,           /* p1 - socket characteristics  */
                           0,                   /* p2                           */
                           0,                   /* p3                           */
                           0,                   /* p4                           */
                           0,                   /* p5                           */
                           0                    /* p6                           */
                         );

        if ( status & STS$M_SUCCESS )
            status = iosb.status;

        if ( !(status & STS$M_SUCCESS) )
            {
            printf( "Failed to create socket\n" );
            exit( status );
            }

        /*
         * connect to specified host and port number
         */

        printf( "Initiated connection to host: %s, port: %d\n",
                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)
              );
        status = sys$qiow( EFN$C_ENF,           /* event flag                   */
                           channel,             /* i/o channel                  */
                           IO$_ACCESS,          /* i/o function code            */
                           &iosb,               /* i/o status block             */
                           0,                   /* ast service routine          */
                           0,                   /* ast parameter                */
                           0,                   /* p1                           */
                           0,                   /* p2                           */
                           &addr_itemlst,       /* p3 - remote socket name      */
                           0,                   /* p4                           */
                           0,                   /* p5                           */
                           0                    /* p6                           */
                         );

        if ( status & STS$M_SUCCESS )
            status = iosb.status;

        if ( !(status & STS$M_SUCCESS) )
            {
            printf( "Failed to connect to server\n" );
            exit( status );
            }

        /*
         * read data from connection
         */

        status = sys$qiow( EFN$C_ENF,           /* event flag                   */
                           channel,             /* i/o channel                  */
                           IO$_READVBLK,        /* i/o function code            */
                           &iosb,               /* i/o status block             */
                           0,                   /* ast service routine          */
                           0,                   /* ast parameter                */
                           buf,                 /* p1 - buffer address          */
                           buflen,              /* p2 - buffer length           */
                           0,                   /* p3                           */
                           0,                   /* p4                           */
                           0,                   /* p5                           */
                           0                    /* p6                           */
                         );

        if ( status & STS$M_SUCCESS )
            status = iosb.status;

        if ( !(status & STS$M_SUCCESS) )
            {
            printf( "Failed to read data from connection\n" );
            exit( status );
            }

        buf[iosb.bytcnt] = 0;
        printf( "Data received: %s\n", buf );

        exit( EXIT_SUCCESS );
    }

You can also specify a list of read buffers by omitting the p1 and p2 arguments and passing the list of buffers as the p6 parameter. See Section 5.5.2 for more information.

# 2.10 Receiving IP Multicast Datagrams

Before a host can receive (read) IP multicast datagrams destined for a particular multicast group other than `all hosts group` , the application must direct the host it is running on to become a member of that multicast group.

To join a group or drop membership from a group, specify the following options. Make sure you include the IN.H header file.

- To join a multicast group, specify the appropriate option to the `setsockopt()` system call:
  - IP_ADD_MEMBERSHIP (Sockets API)
  - TCPIP$C_IP_ADD_MEMBERSHIP (System Services)
  For example:

      struct ip_mreq mreq;
      if (setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, & mreq,
                 sizeof(mreq)) == -1)
                 perror("setsockopt");

  The `mreq` variable has the following structure:

      struct ip_mreq {
                 struct in_addr (imr_multiaddr); /* IP multicast address of group */
                 struct in_addr (imr_interface); /* local IP address of interface */
      };

  In this structure, `imr_interface` can be specified as INADDR_ANY, which allows an application to choose the default multicast interface.
  Each multicast group membership is associated with a particular interface, and multiple interfaces can join the same group. Alternatively, specifying one of the host's local addresses allows an application to select a particular, multicast-capable interface. The maximum number of memberships that can be added on a single socket is subject to the IP_MAX_MEMBERSHIPS value, which is defined in the IN.H header file.
  If multiple sockets request that a host join a multicast group, the host remains a member of that multicast group until the last of those sockets is closed.
- To drop membership from a multicast group, specify the appropriate option to the `setsockopt()` system call:
  - IP_DROP_MEMBERSHIP (Sockets API)
  - TCPIP$C_IP_DROP_MEMBERSHIP (System Services)
  For example:

      struct ip_mreq mreq;
      if (setsockopt( sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq,
               sizeof(mreq))== -1)
      perror("setsockopt");

  The `mreq` variable contains the same structure values used for adding membership.

To receive multicast datagrams sent to a specific UDP port, the receiving socket must have been bound to that port using the $QIO(IO$_SETMODE) system service function or the `bind()` Sockets API function. More than one process can receive UDP datagrams destined for the same port if the function is preceded by a `setsockopt()` system call that specifies the SO_REUSEPORT option.

For example:

    int setreuse = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &setreuse,
             sizeof(setreuse)) == -1)
     perror("setsockopt");

When the `SO_REUSEPORT` option is set, every incoming multicast or broadcast UDP datagram destined for the shared port is delivered to all sockets bound to that port.

Delivery of IP multicast datagrams to SOCK_RAW sockets is determined by the protocol type of the destination.

# 2.11 Reading Out-of-Band Data (TCP Protocol)

Only stream-type (TCP/IP) sockets can receive out-of-band (OOB) data. Upon receiving a TCP/IP OOB character, TCP/IP Services stores a pointer in the received stream to the character that precedes the OOB character.

A read operation with a user buffer size larger than the size of the received stream up to the OOB character completes by returning to the user the received stream up to, but not including, the OOB character.

Poll the socket to determine whether additional read operations are needed before getting all the characters from the stream that precedes the OOB character.

## 2.11.1 Reading OOB Data (Sockets API)

You can use the `recv()` socket function with the MSG_OOB flag set to receive out-of-band data regardless of how many of the preceding characters in the stream you have received.

Example 2-17 shows a TCP server using the `recv()` function to receive out-of-band data.

Example 2-17 Reading OOB Data (Sockets API)

            retval = recv(sock_3,(1)
     message,(2)
    sizeof(message),(3)
    flag);(4)

            if (retval == -1)
                    {
                perror ("receive");
                cleanup( 2, sock_2, sock_3);
                    }
            else
                printf (" %s\n", message);

1. `sock_3` specifies that OOB data is received from socket 2.
2. `message` points to the read buffer where the data is placed.
3. `sizeof (message)` indicates the size of the read buffer.
4. `flag` , when set to MSG_OOB, indicates that OOB data is being received in the specified buffer.

## 2.11.2 Reading OOB Data (System Services)

To receive OOB data from a remote process, use the IO$_READVBLK function with the IO$M_INTERRUPT modifier.

To poll the socket, use a $QIO command with the IO$_SENSEMODE function and the TCPIP$C_IOCTL subfunction that specifies the SIOCATMARK operation.

If the SIOCATMARK returns a value of 0, use additional read QIOs to read more data before reading the OOB character. If the SIOCATMARK returns a value of 1, the next read QIO returns the OOB character.

These functions are useful if a socket has the OOBINLINE socket option set. The OOB character is read with the characters in the stream (IO$_READVBLK) but is not read before the preceding characters. To determine whether or not the first character in the user buffer on the next read is an OOB, poll the socket.

To get a received OOB character for a socket with the socket option OOBINLINE clear, use one of the following functions:

- $QIO with the function IO$_READVBLK|IO$M_INTERRUPT
- IO$_READVBLK with the P4 parameter TCPIP$C_MSG_OOB flag set

Example 2-18 shows how to use the IO$M_INTERRUPT modifier to read out-of-band data.

Example 2-18 Reading OOB Data (System Services)

    /*
    **  Attempt to receive the OOB data from the client.
    **  Use the function code of IO$_READVBLK, passing the address of the
    **  input buffer to P1, and the OOB code, TCPIP$C_MSG_OOB, to P4.
    **  We support the sending and receiving of a one byte of OOB data.
    */
            sysSrvSts = sys$qiow( 0,               /* efn.v | 0              */
                                  IOChanClient,    /* chan.v                 */
                                  IO$_READVBLK,    /* func.v                 */
                                  &iosb,           /* iosb.r | 0             */
                                  0, 0,            /* astadr, astprm: UNUSED */
                                  &OOBBuff,        /* p1.r IO buffer         */
                                  MaxBuff,         /* p2.v IO buffer  size   */
                                  0,               /* p3 UNUSED              */
                                  TCPIP$C_MSG_OOB, /* p4.v IO options flag   */
                                  0, 0             /* p5, p6 UNUSED          */
                                );
            if((( sysSrvSts & 1  ) != 1 ) || /* Validate the system service. */
               ((  iosb.cond_value & 1  ) != 1))  /* Validate the IO status. */
                {
                cleanup( IOChanClient );
                cleanup( IOChannel );
                errorExit( sysSrvSts, iosb.cond_value );
                }
            else
                if( iosb.count == 0 )
                    printf( "    FAILED to receive the message, no connection.\n" );
                else
                    printf( "    SUCCEEDED in receiving '%d'\n", OOBBuff );

# 2.12 Peeking at Queued Messages

You can use a read operation to look at data in a socket receive queue without removing the data from the buffer. This is called peeking.

## 2.12.1 Peeking at Data (Sockets API)

Use the MSG_PEEK flag with the `recv()` function to peek at data in the socket receive queue. Example 2-19 shows a TCP server using the `recv()` function with the MSG_PEEK flag to peek at received data.

Example 2-19 Peeking at Data (Sockets API)

    #include <in.h>                     /* define internet related constants,   */
                                        /* functions, and structures            */
    #include <inet.h>                   /* define network address info          */
    #include <netdb.h>                  /* define network database library info */
    #include <socket.h>                 /* define BSD socket api                */
    #include <stdio.h>                  /* define standard i/o functions        */
    #include <stdlib.h>                 /* define standard library functions    */
    #include <string.h>                 /* define string handling functions     */
    #include <unixio.h>                 /* define unix i/o                      */

    #define BUFSZ           128             /* user input buffer size           */
    #define SERV_BACKLOG    1               /* server backlog                   */
    #define SERV_PORTNUM    1234            /* server port number               */

    int main( void )
    {
        char buf[BUFSIZ];                   /* user input buffer                */
        int conn_sockfd;                    /* connection socket descriptor     */
        int listen_sockfd;                  /* listen socket descriptor         */

        int optval = 1;                     /* SO_REUSEADDR'S option value (on) */

        unsigned int cli_addrlen;           /* returned length of client socket */
                                            /* address structure                */
        struct sockaddr_in cli_addr;        /* client socket address structure  */
        struct sockaddr_in serv_addr;       /* server socket address structure  */

        /*
         * initialize client's socket address structure
         */

        memset( &cli_addr, 0, sizeof(cli_addr) );

        /*
         * initialize server's socket address structure
         */

        memset( &serv_addr, 0, sizeof(serv_addr) );
        serv_addr.sin_family      = AF_INET;
        serv_addr.sin_port        = htons( SERV_PORTNUM );
        serv_addr.sin_addr.s_addr = INADDR_ANY;

        /*
         * create a listen socket
         */

        if ( (listen_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
            {
            perror( "Failed to create socket" );
            exit( EXIT_FAILURE );
            }

        /*
         * bind server's ip address and port number to listen socket
         */

        if ( setsockopt(listen_sockfd,
                        SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0 )
            {
            perror( "Failed to set socket option" );
            exit( EXIT_FAILURE );
            }
        if ( bind(listen_sockfd,
                  (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 )
            {
            perror( "Failed to bind socket" );
            exit( EXIT_FAILURE );
            }

        /*
         * set socket as a listen socket
         */

        if ( listen(listen_sockfd, SERV_BACKLOG) < 0 )
            {
            perror( "Failed to set socket passive" );
            exit( EXIT_FAILURE );
            }

        /*
         * accept connection from a client
         */

        printf( "Waiting for a client connection on port: %d\n",
                ntohs(serv_addr.sin_port)
              );

        cli_addrlen = sizeof(cli_addr);

        conn_sockfd = accept( listen_sockfd,
                              (struct sockaddr *) &cli_addr,
                              &cli_addrlen
                            );
        if ( conn_sockfd < 0 )
            {
            perror( "Failed to accept client connection" );
            exit( EXIT_FAILURE );
            }

        /*
         * ask client to pick a character
         */

        sprintf( buf, "Please pick a character: \r\n");

        if ( send(conn_sockfd, buf, strlen(buf), 0) != strlen(buf) )
            {
            perror( "Failed to write data to connection" );
            exit( EXIT_FAILURE );
            }

        /*
         * peek at client's reply
         */
        if ( recv(conn_sockfd (1)
    , buf (2)
    , 1 (3)
    , MSG_PEEK (4)
    ) != 1 )
            {
            perror( "Failed to read data from connection" );
            exit( EXIT_FAILURE );
            }

        sprintf( buf, "Before receiving, I see you picked '%c'.\r\n", buf[0] );

        if ( send(conn_sockfd, buf, strlen(buf), 0) != strlen(buf) )
            {
            perror( "Failed to write data to connection" );
            exit( EXIT_FAILURE );
            }

        /*
         * now, read client's reply
         */

        if ( recv(conn_sockfd, buf, 1, 0) != 1 )
            {
            perror( "Failed to read data from connection" );
            exit( EXIT_FAILURE );
            }

        sprintf( buf, "Sure enough, I received '%c'.\r\n", buf[0] );

        if ( send(conn_sockfd, buf, strlen(buf), 0) != strlen(buf) )
            {
            perror( "Failed to write data to connection" );
            exit( EXIT_FAILURE );
            }

        /*
         * close sockets
         */

        if ( close(conn_sockfd) < 0 )
            {
            perror( "Failed to close socket" );
            exit( EXIT_FAILURE );
            }

        if ( close(listen_sockfd) < 0 )
            {
            perror( "Failed to close socket" );
            exit( EXIT_FAILURE );
            }

        exit( EXIT_SUCCESS );
    }

Previous | Next | Contents | Index