6.2. The select() API call

The most important API to use when trying to work with multiple clients in the same instance of the same program is very definitely the select() API.

The IBM manual page for the select() API is found here: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/sselect.htm

The manual tells us that the C language prototype for select() looks like this:

                int select(int max_descriptor,
                           fd_set *read_set,
                           fd_set *write_set,
                           fd_set *exception_set,
                           struct timeval *wait_time);
     

This tells us that the select() API accepts 5 parameters, and they are an integer, a pointer to a 'fd_set', another pointer to an 'fd_set', another pointer to an 'fd_set' and a pointer to a 'timeval' structure.

Therefore, the RPG prototype for the select() API looks like this:

         D select          PR            10I 0 extproc('select')
         D   max_desc                    10I 0 VALUE
         D   read_set                      *   VALUE
         D   write_set                     *   VALUE
         D   except_set                    *   VALUE
         D   wait_time                     *   VALUE
     

Read the IBM page over, the information here should give you a basic idea of what exactly the select() API does.

The 'fd_set' data type is a string of 224 bits, each representing a descriptor to check for data in. In C, the fd_set data type is represented as an array of integers. The reason for this is that it's easy to bits on & off in an integer variable in C. In RPG, however, I prefer to work with a simple string of character variables, since that's what the bitwise operations in RPG work with.

In C, there are 4 'preprocessor macros' that are included to make it easy to work with the data in a descriptor set. In order to do the same thing in RPG, we'll have to write subprocedures of our own that mimic these macros.

I'll describe the 'fd_set' data type, and the subprocedures that work with it in the next topic. Suffice it to say, for now, that I use a descriptor set that's defined as '28A' in RPG.

The last parameter to select() is of type 'struct timeval'. You could call it a 'time value structure' if you like.

The 'timeval' structure in C looks like this:

       struct timeval {
          long  tv_sec;                  /* seconds       */
          long  tv_usec;                 /* microseconds  */
        };
     

It's nothing more than 2 32-bit integers in a structure. The first integer is the number of seconds to wait before the select() operation times out. The second integer specifies the number of microseconds (1,000,000th of a second) to wait before timing out.

So, the timeval structure in RPG would look like this:

         D p_timeval       S               *
         D timeval         DS                  based(p_timeval)
         D   tv_sec                      10I 0   
         D   tv_usec                     10I 0
     

Make sure you add the prototype for the select() API as well as the definition of the 'timeval' structure to your SOCKET_H header file.

You call the select() API in RPG like this:

         D readset         S             28A
         D writeset        S             28A
         D excpset         S             28A
         D tv_len          S             10I 0
         D tv              S               *
    
          * create a timeval struct, set it to 10.5 seconds:
    
         C                   eval      tv_len = %size(timeval)
         C                   alloc     tv_len        tv
         C                   eval      p_timeval = tv
    
         C                   eval      tv_sec = 10
         C                   eval      tv_usec = 500000
       
          * call select.
    
         C                   eval      rc = select(maxsock+1: %addr(readset):
         c                                    %addr(writeset): %addr(excpset):
         c                                    p_timeval)