5.2. The bind() API call

Now that we've discussed the basic operation of a server program, let's start getting into specifics. You already know how to call getservbyname(), and socket(), so the first thing we need to know is bind().

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

It tells us that the C language prototype for bind looks like this:

       int bind(int socket_descriptor,
                     struct sockaddr *local_address,
                     int address_length);
     

The procedure is called 'bind'. It takes 3 parameters, an integer, a pointer to a sockaddr structure, and another integer. The API also returns an integer.

So, we'll add the following prototype to our SOCKET_H /copy member:

         D bind            PR            10I 0 ExtProc('bind')
         D   socket                      10I 0 Value
         D   local_addr                    *   Value
         D   addresslen                  10I 0 Value
     

In fact, bind() and connect() have the same exact prototype, (other than the procedure name.) So what's the difference between them?

Short answer: bind() specifies the address & port on the local side of the connection. connect() specifies the address & port of the remote side of the connection.

Long answer:

Each time an IP datagram containing TCP data is sent over the network, the datagram contains a 'local address', 'remote address', 'local port', and 'remote port'. This is the only information that IP has to figure out who ends up getting the packet.

So, both the client and the server port numbers need to be filled in before the connection can work. Data that is directed to the server needs a 'destination' port, so that the data can get sent to the appropriate program running on the server. Likewise, it needs a 'source' so that the server knows who to send data back to, and also so that if there are many connections from the same computer, the server can keep them separate by looking at the source port number.

Since the connection is initiated by the client program, the client program needs to know the server's port number before it can make a connection. For this reason, servers are placed on 'well-known' port numbers. For example, a telnet server is always on port 23. A http server is always on port 80.

The bind() API call assigns the 'local' port number. That is, the port number that is used as the 'source port' on outgoing datagrams, and the 'destination port' on incoming datagrams. The connect() API call assigns the 'remote' port number, which is the one that is the 'destination port' on outgoing packets and the 'source port' on incoming packets.

Okay, back to bind():

If you don't call bind(), the operating system will automatically assign you an available port number. In the case of our client programs, we didn't call the bind() API, and the operating system assigned us whatever port was available. It didn't matter which one, since we were the client and we were initiating the connection. The server would know our port number because we were sending it to the server in every IP datagram that we sent.

For a server, however, we must use a 'well-known' port number. Without it, no clients will ever find us! Therefore, a server program will invariably call the bind() API.

There are some named constants that we will want to use with bind(), so we'll also want to define those in our SOCKET_H /copy member. They look like this:

         D INADDR_ANY      C                   CONST(0)
         D INADDR_BROADCAST...
         D                 C                   CONST(4294967295)
         D INADDR_LOOPBACK...
         D                 C                   CONST(2130706433)
     

These constants are special values that can be assigned to the sin_addr parameter of the sockaddr_in structure. (You'll recall that sockaddr_in is to be used in place of the sockaddr structure when doing TCP/IP related socket calls.)

The value "INADDR_ANY" means that we will bind to any/all IP addresses that the local computer currently has. Lets say that your AS/400 is dialed into the Internet using a modem and the PPP protocol. The PPP interface is given an IP address by your ISP. If you also have a network card, and a TCP/IP connection to your LAN, you'll have an address for that interface as well. Also, the special TCP/IP interface called 'loopback' has it's own special address (127.0.0.1). If you use the value 'INADDR_ANY' when calling bind(), you will be able to accept connections from people connecting to any of these addresses.

On the other hand, if you only want to allow people on your LAN to connect, you could bind specifically to the network card's IP address. Likewise, if you only wanted to allow connections from programs on the same computer, you could specify 'INADDR_LOOPBACK'.

Whew... after all that explanation, calling bind() should be easy. :)

         D/copy socktut/qrpglesrc,socket_h
    
         D bindto          S               *
         D addrlen         S             10I 0
         
          ** reserve memory for a sockaddr_in structure:
         c                   eval      addrlen = %size(sockaddr_in)
         c                   alloc     addrlen       bindto
         
          ** set sockaddr_in structure so that we can receive connections
          **   on port number "port_number" on any address.  
         c                   eval      p_sockaddr = bindto
         c                   eval      sin_family = AF_INET
         c                   eval      sin_addr = INADDR_ANY
         c                   eval      sin_port = port_number
         c                   eval      sin_zero = *ALLx'00'
         
          ** bind the socket!
         c                   if        bind(socket: bindto: addrlen) < 0
         C* bind() failed.  check errno
         c                   endif