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