We now have our socket open to the world, and we’re listening for connections. The C code (libdill) and the operating system will do the work of making the connection happen, so we don’t have to worry about that part. Our job here is to accept an incoming connection and do something with it (send a message, for example) once it’s established. Back to the libdill documentation; tcp_accept accepts an incoming TCP connection:
int tcp_accept( ❶
int s, ❷
struct ipaddr* addr, ❸
int64_t deadline); ❹
❶ This function will return an int.
❷ The first argument is the socket created by tcp_listen.
❸ The second argument is the IP address of the remote connection.
❹ The third argument is a deadline in milliseconds.
The first argument (input), s, is the socket created by tcp_listen. The second argument (output) is the IP address structure associated with the remote client–its value will tell us where the connection is coming from. Finally, the deadline is a point in time when the operation should time out (abort). According to the libdill documentation, deadline having a value of 0 means an immediate timeout, that is, return immediately if the function doesn’t succeed. The value of -1 means no deadline–block forever until the function succeeds. For our simple example, blocking until success (deadline = -1) will do just fine, and we’ll use this value for the deadline through the rest of this chapter. Like before, pay close attention to which arguments are declared as pointers (ipaddr*, pass by reference), and which aren’t (s and deadline, pass by value).
This is the first time that we encounter the int64_t data type. In C parlance, int64_t (short for 64-bit-wide integer type) is known as a long long. It’s a signed integer type that occupies exactly 8 bytes (64 bits) in memory and is useful for variables whose values can get extraordinarily large, up to about +/- 9 × 1018. Like other C-equivalent type kinds, int64_t is available to Fortran through the iso_c_binding module as c_int64_t:
use iso_c_binding, only: c_int64_t
Here’s the complete Fortran interface to tcp_accept:
integer(c_int) function tcp_accept(s, addr, deadline) & ❶
bind(c, name='dill_tcp_accept') ❷
import :: c_int, c_int64_t, ipaddr ❸
integer(c_int), value, intent(in) :: s ❹
type(ipaddr), intent(out) :: addr ❺
integer(c_int64_t), value, intent(in) :: deadline ❻
end function tcp_accept
❶ This function will return a c_int.
❷ We’ll bind it to dill_tcp_accept.
❸ Imports the C-equivalent type kinds into the local scope
❹ The first argument is the c_int socket and is passed by value.
❺ The second argument is the ipaddr struct and is passed by reference.
❻ The third argument is the c_int64_t deadline in milliseconds and is passed by value.
We want the server program to keep accepting connections indefinitely, so we’ll place tcp_accept in an infinite do loop back in our server program in server.f90:
do ❶
connection = tcp_accept(socket, addr, -1_c_int64_t) ❷
call ipaddr_str(addr, address_string) ❸
print *, 'New connection from ' &
// trim(address_string)
end do
❷ Accepts an incoming connection and gets its IP address information
❸ Logs to the screen the IP address of an incoming connection
This snippet is the core of the server program. We started listening for connections on the socket that we opened, and now we’ll accept incoming connections to this socket. To keep accepting connections one after another, we place this code inside of an infinite do loop. As soon as one incoming connection is accepted and processed, the server will wait for the next one. This also means that the program has no way of stopping on its own, except for unhandled exceptions; otherwise, it would need to be interrupted by the user from the OS (for example, with Ctrl + C in Linux).
Here, we also reuse the ipaddr_str interface we implemented earlier to get the text representation of the IP address of the incoming connection. As remote clients connect to your server, you’ll be able to see from which IP addresses the connections are coming. Note that at this point, the server program is an intermediate implementation and missing a critical piece–sending a response back to each incoming connection.