Chapter 3. Socket API calls to create our first client program

Table of Contents
3.1. The socket() API call
3.2. The connect() API call
3.3. The send() and recv() API calls
3.4. Translating from ASCII to EBCDIC
3.5. The close() API call
3.6. Our first client program
Written by Scott Klement.

3.1. The socket() API call

The previous sections explained how to find out the port number for a service name, and how to get the IP address for a host name. This section will utilize that information to create a simple client program.

The socket() API is used to create a socket. You can think of a socket as being the virtual device that is used to read & write data from a network connection.

The IBM manual page that documents the socket API is at this link:

It lists the following as the prototype for the socket() API:

              int socket(int address_family,
                          int type,
                          int protocol)

This tells us that the name of the procedure to call is 'socket', and that it accepts 3 parameters. Each one is an integer, passed by value. It also returns an integer. Therefore, the RPG prototype for the socket() API looks like this:

         D socket          PR            10I 0 ExtProc('socket')
         D  addr_family                  10I 0 value
         D  type                         10I 0 value
         D  protocol                     10I 0 value

It's important to realize that the socket APIs can be used for other networking protocols besides TCP/IP. When we create a socket, we need to explain to the socket API that we wish to communicate using the IP protocol, and that we wish to use TCP on top of the IP protocol.

For address family, the manual tells us that we need to specify a value of 'AF_INET' if we wish to do network programming in the 'Internet domain'. Therefore, when we specify a value of 'AF_INET', what we're really telling the API is to 'use the IP protocol'.

Under the 'type' parameter, it allows us to give values of 'SOCK_DGRAM', 'SOCK_SEQPACKET', 'SOCK_STREAM' or 'SOCK_RAW'. The TCP protocol is the standard streaming protocol for use over IP. So, if we say 'SOCK_STREAM', we'll use the TCP protocol. As you might imagine, SOCK_DGRAM is used for the UDP protocol and SOCK_RAW is used for writing raw IP datagrams.

Finally, we specify which protocol we wish to use with our socket. Note that, again, we can specify IPPROTO_TCP for TCP, IPPROTO_UDP for UDP, etc. However, this isn't necessary! Because we already specified that we wanted a 'stream socket' over 'internet domain', it already knows that it should be using TCP. Therefore, we can specify 'IPPROTO_IP' if we want, and the API will use the default protocol for the socket type.

Now, we just have one problem: We don't know what integer values AF_INET, SOCK_STREAM and IPPPROTO_IP are! IBM is referencing named constants that they've defined in the appropriate header files for C programs, but we don't have these defined for us in RPG! But, if you do a bit of snooping into the 'System Openness Includes' library, you'll find that AF_INET is defined to be '2', SOCK_STREAM is defined to be '1', and IPPROTO_IP is defined as '0'. To make this easier for us, we'll make named constants that match these values, like so:

        D AF_INET         C                   CONST(2)
        D SOCK_STREAM     C                   CONST(1)
        D IPPROTO_IP      C                   CONST(0)

Now we can call the socket() API like so:

         c                   eval      s = socket(AF_INET:SOCK_STREAM:IPPROTO_IP)