3.2. The connect() API call

Once we have a socket to work with, we need to connect it to something. We do that using the connect() API, which is documented in IBM's manual at this location: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/connec.htm

It tells us here that the prototype for the connect() API looks like this:

         int connect(int socket_descriptor,
                     struct sockaddr *destination_address,
                     int address_length)
     

So, as you can see, the procedure is named 'connect', and it accepts 3 parameters. An integer, a pointer to a 'sockaddr' structure, and another integer. It also returns an integer. This means that the RPG prototype will look like this:

         D connect         PR            10I 0 ExtProc('connect')
         D  sock_desc                    10I 0 value
         D  dest_addr                      *   value
         D  addr_len                     10I 0 value
     

Looking further down the manual, we see that the a 'sockaddr' structure is defined as follows:

                     struct sockaddr {
                        u_short sa_family;
                        char    sa_data[14];
                     };
     

Remember, the purpose of this structure is to tell the API which IP address and port number to connect to. Why, then, doesn't it contain fields that we can put the address and port numbers into? Again, we have to remember that the socket APIs can work with many different network protocols. Each protocol has a completely different format for how addresses work. This 'sockaddr' structure is, therefore, a generic structure. It contains a place to put the identifying address family, along with a generic "data" field that the address can be placed in, regardless of the format of the address.

Although it's not documented on IBM's page for the connect() API, there is actually a different structure called 'sockaddr_in' which is designed especially for internet addresses. The C definition for sockaddr_in can be found in the file QSYSINC/NETINET, member IN, if you have the System Openness Includes loaded. It looks like this:

         struct sockaddr_in {            /* socket address (internet)   */
             short  sin_family;          /* address family (AF_INET)    */
           u_short  sin_port;            /* port number                 */
            struct  in_addr  sin_addr;   /* IP address                  */
              char  sin_zero[8];         /* reserved - must be 0x00's   */
         };
     

To make it easier to use these structures in RPG, I like to make them based in the same area of memory. This means that you can look at the data as a 'sockaddr', or the same data as a 'sockaddr_in' without moving the data around. Having said that, here's the definition that I use for the sockaddr & sockaddr_in structures:

         D p_sockaddr      S               *
         D sockaddr        DS                  based(p_sockaddr)
         D   sa_family                    5I 0
         D   sa_data                     14A  
         D sockaddr_in     DS                  based(p_sockaddr)
         D   sin_family                   5I 0
         D   sin_port                     5U 0 
         D   sin_addr                    10U 0
         D   sin_zero                     8A  
     

Before we can call the connect() API, we need to ask the operating system for some memory that we can store our sockaddr structure into. Then, we can populate the sockaddr_in structure, and actually call the connect() API. Like so:

         DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++
         D p_connto        S               *
         D addrlen         S             10I 0
    
         C* Ask the operating system for some memory to store our socket
         C* address into:
         C                   eval      addrlen = %size(sockaddr)
         C                   alloc     addrlen       p_connto
    
         C* Point the socket address structure at the newly allocated
         C*   area of memory:
         C                   eval      p_sockaddr = p_connto
     
         C* Populate the sockaddr_in structure
         C*  Note that IP is the ip address we previously looked up
         C*  using the inet_addr and/or gethostbyname APIs
         C*  and port is the port number that we looked up using the
         C*  getservbyname API.
         C                   eval      sin_family = AF_INET
         C                   eval      sin_addr = IP
         C                   eval      sin_port = PORT
         C                   eval      sin_zero = *ALLx'00'
    
         C*  Finally, we can connect to a server:
         C                   if        connect(s: p_connto: addrlen) < 0
         C*** Connect failed, report error here
         C                   endif