Like we did on the server, first we need to initialize a data structure to hold the IP address and port number. In this case, they’ll be the IP address and port number of the remote host to which we’ll connect. Here’s the prototype of the ipaddr_remote function in libdill:
int ipaddr_remote( ❶
struct ipaddr* addr, ❷
const char* name, ❸
int port, ❹
int mode, ❹
int64_t deadline); ❺
❶ This function returns an int.
❷ Pointer to the ipaddr struct (output argument)
❸ Pointer to the IP address name character string
❹ Integer port number and mode (IP v4 or v6)
This interface is exactly the same as for ipaddr_local, except for the deadline argument. As we’ve implemented this argument in other interfaces, the Fortran interface to ipaddr_remote is straightforward:
integer(c_int) function ipaddr_remote(addr, name, port, mode, deadline) &
bind(c, name='dill_ipaddr_remote')
import :: c_char, c_int, c_int64_t, ipaddr
type(ipaddr), intent(out) :: addr
character(c_char), intent(in) :: name(*)
integer(c_int), value, intent(in) :: port
integer(c_int), value, intent(in) :: mode
integer(c_int64_t), value, intent(in) :: deadline
end function ipaddr_remote
As in the case of the interface to ipaddr_local, which we implemented in section 11.2.2, here we need to watch out for which arguments are passed by reference (pointer to the address in memory), and which by value (a local copy of the argument is made inside the function). Recall that by default, Fortran passes arguments by reference, so any arguments in the C function that are declared with * (denoting them as pointers), can be declared as is in the Fortran interface. However, for the arguments in the C function that aren’t declared as pointers, we must use the value attribute in the Fortran interface. First, here’s the prototype of the C tcp_connect function:
int tcp_connect(
const struct ipaddr* addr,
int64_t deadline);
And here’s its Fortran sibling interface to tcp_connect:
integer(c_int) function tcp_connect(addr, deadline) &
bind(c, name='dill_tcp_connect')
import :: c_int, c_int64_t, ipaddr
type(ipaddr), intent(in) :: addr
integer(c_int64_t), value, intent(in) :: deadline
end function tcp_connect
At this point, we can use these interfaces to initialize the ipaddr type and connect to a remote socket, as shown in the following listing.
Listing 11.8 Fortran TCP client that creates a connection to a remote server
program client
use iso_c_binding, only: c_int, c_null_char, & ❶
c_int64_t ❶
use mod_dill, only: ipaddr, ipaddr_remote, & ❷
IPADDR_IPV4, tcp_connect ❷
implicit none
integer(c_int) :: rc, connection
type(ipaddr) :: addr
rc = ipaddr_remote(addr, & ❸
'127.0.0.1' // c_null_char, & ❸
5555_c_int, & ❸
IPADDR_IPV4, & ❸
-1_c_int64_t) ❸
connection = tcp_connect(addr, -1_c_int64_t) ❹
end program client
❶ Imports C-type kind parameters from the built-in module
❷ Imports our Fortran interfaces to libdill functions
❸ Initializes the remote IP address and port number
❹ Creates a new connection to the remote server
Similar to the server program, the client begins by initializing the IP address data structure. This time, it’s the IP address and port number of the remote host that we’re connecting to. As you can see, our remote IP address is the same as the local one initialized in the server program. They’re the same because we’re running both the client and the server on the same computer, which is a common practice during development.
You can place this program in a new source file; for example, client.f 90. Let’s compile and run it:
gfortran -c mod_dill.f90 ❶
gfortran client.f90 -o client libdill.a -pthread ❷
./client ❸
❶ Compiles the mod_dill module file
❷ Compiles and links the client program
On its own, nothing should come out of this. Our client simply initializes the remote IP address and connects to a socket at that address. We’re also not doing any error checks (by testing the values of rc or connection), so we don’t even know if these function calls succeeded.
Now, open a new terminal session and run the server program in it. From the client terminal, run the client program. You’ll see a message come up in the server terminal:
./server ❶
Listening on socket: ❷
IP address: 127.0.0.1 ❷
Port: 5555 ❷
New connection from 127.0.0.1 ❸
❸ Server output on new connection from remote client