Greetings,
I’d like to announce the release of fortran-nng, a collection of Fortran 2018 interface bindings to the open-source messaging library NNG:
NNG, like its predecessors nanomsg (and to some extent ZeroMQ), is a lightweight, broker-less library, offering a simple API to solve common recurring messaging problems, such as publish/subscribe, RPC-style request/reply, or service discovery. The API frees the programmer from worrying about details like connection management, retries, and other common considerations, so that they can focus on the application instead of the plumbing.
The bindings are unopinionated and stay close to the C API. NNG supports a range of communication patterns, for instance:
- Pipeline: one-way pipe for producer/consumer problems, including load-balancing.
- Request/Reply: reliable synchronous communication for RPC and load balancing (I ask, you answer).
- Pair: one-to-one peer connection (two-way radio).
- Pub/Sub: allows a single broadcaster to publish messages to many subscribers (topics & broadcast).
- Survey: for timed surveys (everybody votes) and service discovery.
- Bus: for routing and fully interconnected mesh networks.
Basic demonstrations ported from C to Fortran are provided in the repository. In the following example, a server routine uses the surveyor pattern to query connected clients for a time stamp. (Error handling has been omitted.)
subroutine server(url)
character(*), intent(in) :: url
character(80), target :: buffer
integer :: rc
integer(c_size_t) :: sz
type(nng_listener) :: listener
type(nng_socket) :: socket
rc = nng_surveyor0_open(socket) ! Open socket.
rc = nng_listen(socket, f_c_str(url), listener, 0) ! Listen for connections.
do
buffer = 'DATE'
print '("SERVER: SENDING ", a, " SURVEY REQUEST")', trim(buffer)
! Send request.
sz = len_trim(buffer, c_size_t)
rc = nng_send(socket, c_loc(buffer), sz, 0)
do
! Collect responses.
buffer = ' '
sz = len(buffer, c_size_t)
rc = nng_recv(socket, c_loc(buffer), sz, 0)
if (rc == NNG_ETIMEDOUT) exit
print '("SERVER: RECEIVED ", a, " SURVEY RESPONSE")', trim(buffer)
end do
print '("SERVER: SURVEY COMPLETE")'
end do
rc = nng_socket_close(socket)
end subroutine server
The clients connect to the server and wait for the survey to start, then respond with a character string of the current date and time in ISO 8601 format:
subroutine client(url, name)
character(*), intent(in) :: url
character(*), intent(in) :: name
character(80), target :: buffer
integer :: rc
integer(c_size_t) :: sz
type(nng_dialer) :: dialer
type(nng_socket) :: socket
rc = nng_respondent0_open(socket) ! Open socket.
rc = nng_dial(socket, f_c_str(url), dialer, NNG_FLAG_NONBLOCK) ! Connect.
do
! Wait for survey request.
buffer = ' '
sz = len(buffer, c_size_t)
rc = nng_recv(socket, c_loc(buffer), sz, 0)
print '("CLIENT (", a, "): RECEIVED ", a, " SURVEY REQUEST")', trim(name), trim(buffer)
print '("CLIENT (", a, "): SENDING DATE SURVEY RESPONSE")', trim(name)
! Send date and time in ISO 8601 as survey response.
buffer = iso8601()
sz = len_trim(buffer, c_size_t)
rc = nng_send(socket, c_loc(buffer), sz, 0)
end do
rc = nng_socket_close(socket)
end subroutine client
Compiling the example program:
$ flang -I/opt/include/libfortran-nng -o survey survey.f90 /opt/lib/libfortran-nng.a -lnng
Starting the server and three clients:
./survey server ipc:///tmp/survey.ipc & server=$!
./survey client ipc:///tmp/survey.ipc client0 & client0=$!
./survey client ipc:///tmp/survey.ipc client1 & client1=$!
./survey client ipc:///tmp/survey.ipc client2 & client2=$!
sleep 10
kill $server $client0 $client1 $client2
Output:
SERVER: SENDING DATE SURVEY REQUEST
CLIENT (client0): RECEIVED DATE SURVEY REQUEST
CLIENT (client2): RECEIVED DATE SURVEY REQUEST
CLIENT (client1): RECEIVED DATE SURVEY REQUEST
CLIENT (client1): SENDING DATE SURVEY RESPONSE
CLIENT (client2): SENDING DATE SURVEY RESPONSE
CLIENT (client0): SENDING DATE SURVEY RESPONSE
SERVER: RECEIVED 2026-01-03T17:51:33.590+01:00 SURVEY RESPONSE
SERVER: RECEIVED 2026-01-03T17:51:33.590+01:00 SURVEY RESPONSE
SERVER: RECEIVED 2026-01-03T17:51:33.590+01:00 SURVEY RESPONSE
SERVER: SURVEY COMPLETE