Fortran

Guide To Learn

Checking IP address values

Testing is recommended. Having made our call to ipaddr_local, we have no clue whether it worked or not. All that we know is that the program didn’t crash with any error message. After we initialize ipaddr, we can use libdill functions to explicitly check for the IP address and port number values stored.

First, let’s take a look at the IP address. libdill has a function to read its value from an initialized ipaddr struct:

const char* ipaddr_str(             ❶
    const struct ipaddr* addr,      ❷
    char* buf);                     ❸

❶ This function returns a pointer to a character string.

❷ The first argument (input) is an ipaddr struct.

❸ The second argument (input/output) is a character string.

From the libdill documentation, ipaddr_str formats the address as a human-readable string.

  • addr–IP address object
  • buf–Buffer to store the result in, which must be at least IPADDR_MAXSTRLEN bytes long

The function returns the ipstr argument, which is the pointer to the formatted string.

It seems like there are two ways to obtain the IP address string: by accessing the value through the pointer returned by the function, or by reading it directly from the character string, buf. Let’s interface this with a Fortran subroutine that will use the character string buffer to retrieve the IP address string, as shown in the following listing.

Listing 11.4 Fortran interface to ipaddr_str, this time a subroutine

module mod_dill
  ...
  public :: ipaddr, ipaddr_local, ipaddr_str
 
  interface
    ...
    subroutine ipaddr_str(addr, buf) &            ❶
               bind(c, name='dill_ipaddr_str')    ❷
      import :: c_char, ipaddr                    ❸
      type(ipaddr), intent(in) :: addr            ❹
      character(c_char), intent(out) :: buf(*)    ❺
    end subroutine ipaddr_str
 
  end interface
 
end module mod_dill

❶ A subroutine that takes two arguments

❷ Let’s bind this to dill_ipaddr_str.

❸ Imports C-type kind parameters into the local scope

❹ The ipaddr derived type is the input argument.

❺ An array of characters is the output argument.

This is the first interface where we use an array of characters as an argument. You may notice a curious detail: this argument is declared with an asterisk (buf(*)), rather than a colon like we used in the past for assumed-shape array arguments. This is a somewhat obscure feature of Fortran called assumed-size arrays (in contrast to assumed shape) and applies to array or character string arguments to C functions. It’s there for easier interfacing with C functions, but otherwise you shouldn’t ever use it in pure Fortran programs. To quote the late Walter Brainerd in his book Guide to Fortran 2008 Programming : “Do not ask why–just do it.”

Note that we could’ve made this Fortran interface a function as well as a subroutine. A subroutine makes sense when one or more of the arguments are output arguments; however, a subroutine can’t capture the return value of a function. In this case, this is okay because we only care about accessing the buffer buf as the intent(out) argument. The choice between using a function or a subroutine to interface with a C-function also affects the syntax of how the procedure is invoked. Feel free to implement interfaces that best fit your programming style, and make sure that you get the data that you need.

Now that we have a subroutine to get the IP address string from the ipaddr struct, how about the port number? Time for an exercise! (See the “Exercise 1” sidebar.)

Exercise 1: The Fortran interface to ipaddr_port

It’s your turn now! We need to check that the port number is stored correctly when we call the ipaddr_local function. We’ve already implemented the interface to ipaddr_str, which returns the character string representation of the IP address. libdill also provides the ipaddr_port function, which returns the integer value of the port number associated with the ipaddr data structure:

int ipaddr_port(
    const struct ipaddr* addr);

Implement the Fortran interface to this function and check that it returns the correct value of the port number.

The solution to this exercise is given in the “Answer key” section near the end of the chapter.

Finally, in our server program, we can check that the IP address and port number are stored correctly, as shown in the following listing.

Listing 11.5 Updated Fortran TCP server program

program server
 
  use iso_c_binding, only: c_char, c_int, c_null_char      ❶
  use mod_dill, only: ipaddr, ipaddr_local,    &           ❷
                      ipaddr_port, ipaddr_str, &           ❷
                      IPADDR_MAXSTRLEN, IPADDR_IPV4        ❷
 
  implicit none
 
  integer(c_int) :: rc
  type(ipaddr) :: addr
  character(kind=c_char, len=IPADDR_MAXSTRLEN) :: &        ❸
    address_string = ''                                    ❸
 
  rc = ipaddr_local(addr, '127.0.0.1' // c_null_char, &    ❹
                    5555_c_int, IPADDR_IPV4)               ❹
  call ipaddr_str(addr, address_string)                    ❺
 
  print *, 'Opening socket:'                               ❻
  print *, '  IP address: ', address_string                ❻
  print *, '  Port: ', ipaddr_port(addr)                   ❻
 
end program server

❶ Imports C-type parameters from the built-in module

❷ Imports our Fortran-C interfaces

❸ Initializes the address string variable

❹ Initializes the IP address and port number

❺ Gets the IP address string to check its value

❻ Prints the IP address and port number to the screen

You can now compile and run this program, and it should print the IP address and port number to the screen:

gfortran mod_dill.f90 server.f90 libdill.a \    ❶
  -pthread -o server                            ❶
./server                                        ❷
  Opening socket:                               ❸
   IP address: 127.0.0.1                        ❸
   Port:         5555                           ❸

❶ Compiles and links

❷ Runs the server program

❸ The output of the server program

So far, so good! The program compiles successfully and runs without any apparent errors. If you’re wondering about the -pthread flag, it enables the use of POSIX threads, a system dependency of libdill. Even though we don’t use threads in our Fortran code directly, we include this flag to make the linker happy; that is, so we can build the executable file.

POSIX threads

POSIX (Portable Operating System Interface) threads, often called pthreads, is a concurrent and parallel execution model. It’s not intrinsic to any single programming language, but instead is provided by the operating system. It’s supported out of the box by most UNIX systems, including macOS, Linux, and Windows using a third-party library. You can read more about POSIX threads at https://en.wikipedia.org/wiki/ POSIX_Threads.

Checking IP address values

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top