The previous sections described an overview of TCP/IP terminology as well as an overview of calling the sockets API for a TCP connection. This section describes the details of calling "getservbyname()" and explains how to understand the IBM UNIX-type API manual.
The Socket API is described in the IBM Information Center under the category "UNIX-type APIs." Unfortunately, the calling syntax that's described is for people using the ILE C/400 programming language.
The getservbyname() API is documented here: http://publib.boulder.ibm.com/pubs/html/as400/v4r5/ic2924/info/apis/gsrvnm.htm
And it says that in order to call getservbyname, we need to specify parms like this:
struct servent *getservbyname(char *service_name, char *protocol_name)
By now, unless you're familiar with programming in C, you're probably saying "huh? what does that mean?" So, I'll tell you :) Let's look at that statement again, but break it into pieces:
Since names in C are case sensitive, we MUST supply the name 'getservbyname' as all lowercase. (That's important since RPG likes to convert everything to uppercase at compile-time).
So, to define this same prototype in RPG, we need to do this:
DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++ D getservbyname PR * ExtProc('getservbyname') D service_name * value options(*string) D protocol_name * value options(*string)
Note that all pointers passed to C procedures are always passed by "value". The keyword "options(*string)" is an option added to RPG to help make it compatible with C programs. Strings in C end with a "null" character (x'00') that allows it to recognize the end of a variable length string. Specifying options(*string) causes RPG to automatically add this trailing null when it makes the call.
If you look further down, (in the return value section) they describe this "servent" structure by showing us this:
This means that the servent structure contains 4 pieces of data:
So, if we wanted to define this structure in RPG, we'd do it like this:
DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++ D p_servent S * D servent DS based(p_servent) D s_name * D s_aliases * D s_port 10I 0 D s_proto *
As you can probably tell from this reading this page, so far, the hardest part of calling the UNIX-type APIs from RPG programs is converting things from C syntax to RPG syntax. That's why it's so frustrating that IBM doesn't provide a manual and header files specifically for RPG programmers.
For this tutorial, I will show you how to make the conversions from the C prototypes to the RPG prototypes. If you want to learn more about how to do this yourself, you might try this link: http://www.opensource400.org/callc.html
But, anyway... I'll get off the soap box and get back to the tutorial...
Once we have set up this data structure and procedure prototype, we can call the getservbyname procedure simply by coding:
CL0N01Factor1+++++++Opcode&ExtExtended-factor2+++++++++++++++++++++++++ C eval p_servent = getservbyname('http': 'tcp')
And then check the value of s_port to find out which port http is located on. As you can see, defining the prototype and data structure is the hard part. Once we've got that, making the call is easy.
So, here's a simple program that accepts a service name as a parameter, and displays the corresponding port number:
Member: QRPGLESRC, SERVPORT
H DFTACTGRP(*NO) ACTGRP(*NEW) D getservbyname PR * ExtProc('getservbyname') D service_name * value options(*string) D protocol_name * value options(*string) D p_servent S * D servent DS based(p_servent) D s_name * D s_aliases * D s_port 10I 0 D s_proto * D service S 10A D msg S 50A c *entry plist c parm service c eval p_servent = getservbyname( c %trim(service): 'tcp') c if p_servent = *NULL c eval msg = 'No such service found!' c else c eval msg = 'port = ' + %editc(s_port:'L') c endif c dsply msg c eval *inlr = *on
Compile this by typing: CRTBNDRPG SERVPORT SRCFILE(SOCKTUT/QRPGLESRC)
Run it by typing CALL SERVPORT PARM('http')
A few notes: