Finally, on each accepted connection, we’ll send a message to the client to let them know the server is alive. Not so fast, though. Recall that the data is just a stream of bytes, and the TCP protocol defines messages that are specially formatted packets of binary data. Fortunately, libdill provides a convenience function, suffix_attach, to append a suffix to any packet of binary data that we give to it:
int suffix_attach( ❶
int s, ❷
const void* suffix, ❸
size_t suffixlen); ❹
❶ This function returns an int.
❷ Socket to apply the suffix to
This is the first time that we encounter the C void pointer (void*) as one of the arguments. The void pointer is used to allow addressing variables of different types. In that sense, the void pointer is just an address to a place in memory, but that memory could hold an integer, a float, or a character. The const attribute ensures that the value of suffix can’t be modified inside the function, analogous to Fortran’s intent(in) arguments. We could interface the void-typed suffix with any type; however, to format the byte packets into valid TCP messages, we’ll append “\r\n”–a carriage return and a new line–to each packet. To that end, interfacing the argument suffix with a character(c_char) will work fine, using the Fortran interface to the suffix_attach function in libdill:
integer(c_int) function suffix_attach(s, suffix, suffixlen) &
bind(c, name='dill_suffix_attach')
import :: c_char, c_int, c_size_t
integer(c_int), value, intent(in) :: s
character(c_char), intent(in) :: suffix(*)
integer(c_size_t), value, intent(in) :: suffixlen
end function suffix_attach
To formulate the suffix “\r\n” in our Fortran code, we need to import these special characters from iso_c_binding:
...
use iso_c_binding, only: c_null_char, c_new_line, c_carriage_return
character(len=*), parameter :: &
TCP_SUFFIX = c_carriage_return // c_new_line // c_null_char
...
connection = suffix_attach(connection, TCP_SUFFIX, 2_c_size_t)
...
We call the suffix_attach function once for any given connection, and every packet sent through that connection will be formatted as a TCP message. If you’re wondering why the length of TCP_SUFFIX (third argument to suffix_attach) is 2 and not 3, it’s because libdill (and C in general) doesn’t count the null character as a separate character. Even though we pass it here as a three-character string (carriage return, new line, and null character), libdill receives and interprets it as a two-character string.
Now that we have the piece that formats byte packets into valid TCP messages, let’s get to actually sending the message to the client. You know the drill–here’s how libdill defines the msend function.
Listing 11.6 Header of the msend function in libdill
int msend( ❶
int s, ❷
const void* buf, ❸
size_t len, ❹
int64_t deadline); ❺
❶ The function result is an int.
❷ Input socket on which to send the message
And here’s its corresponding Fortran interface:
integer(c_int) function msend(s, buf, len, deadline) &
bind(c, name='dill_msend')
import :: c_char, c_int, c_int64_t, c_size_t
integer(c_int), value, intent(in) :: s
character(c_char), intent(in) :: buf(*)
integer(c_size_t), value, intent(in) :: len
integer(c_int64_t), value, intent(in) :: deadline
end function msend
With these two functions ready, we can now add the new calls to suffix_attach and msend to our server loop to send a TCP message on a remote connection from a client:
do
connection = tcp_accept(socket, addr_remote, -1_c_int64_t)
call ipaddr_str(addr, address_string)
print *, 'New connection from ' // trim(address_string)
connection = suffix_attach(connection, TCP_SUFFIX, 2_c_size_t)
rc = msend(connection, 'Hello' // c_null_char, 5_c_size_t, -1_c_int64_t)
Our connection loop thus far accepts a connection (tcp_accept), parses the remote client IP address (ipaddr_str) and prints it to the screen, attaches a special suffix to format messages following a TCP protocol (suffix_attach), and finally sends a TCP message (msend).
You may have noticed that in the call to msend, we send both the contents of the message as the second argument, and the length of the message as a third argument. At first this may seem tedious and redundant, but it’s necessary due to C semantics and how the msend function is designed in libdill. Recall that the message buffer buf is declared as a void pointer (listing 11.6), which is the memory address where the buffer begins, so msend needs additional information about how long the buffer is. If we were designing a higher level Fortran interface with libdill, we could take an extra step and write a wrapper around the Fortran interface to msend, and automatically calculate the length of the message before passing it to msend. Similarly, such a wrapper could automatically append c_null_char to any string that’s on its way to libdill. This kind of exercise is beyond the scope of this chapter, but I encourage you to practice by implementing higher level wrappers that are more user friendly.