|Did you know ...||Search Documentation:|
|library(udp_broadcast): A UDP broadcast proxy|
SWI-Prolog's broadcast library provides a means that may be used to facilitate publish and subscribe communication regimes between anonymous members of a community of interest. The members of the community are however, necessarily limited to a single instance of Prolog. The UDP broadcast library removes that restriction. With this library loaded, any member on your local IP subnetwork that also has this library loaded may hear and respond to your broadcasts.
This library support three styles of networking as described below. Each of these networks have their own advantages and disadvantages. Please study the literature to understand the consequences.
A broadcast/1 or broadcast_request/1
of the shape
udp(Scope, Term) or
udp(Scope, Term, TimeOut) is forwarded over the UDP network
to all peers that joined the same Scope. To prevent the
potential for feedback loops, only the plain Term is
broadcasted locally. The timeout is optional. It specifies the amount to
time to wait for replies to arrive in response to a broadcast_request/1.
The default period is 0.250 seconds. The timeout is ignored for
An example of three separate processes cooperating in the same scope
Process A: ?- listen(number(X), between(1, 5, X)). true. ?- Process B: ?- listen(number(X), between(7, 9, X)). true. ?- Process C: ?- findall(X, broadcast_request(udp(peers, number(X))), Xs). Xs = [1, 2, 3, 4, 5, 7, 8, 9]. ?-
It is also possible to carry on a private dialog with a single responder. To do this, you supply a compound of the form, Term:PortId, to a UDP scoped broadcast/1 or broadcast_request/1, where PortId is the ip-address and port-id of the intended listener. If you supply an unbound variable, PortId, to broadcast_request, it will be unified with the address of the listener that responds to Term. You may send a directed broadcast to a specific member by simply providing this address in a similarly structured compound to a UDP scoped broadcast/1. The message is sent via unicast to that member only by way of the member's broadcast listener. It is received by the listener just as any other broadcast would be. The listener does not know the difference.
For example, in order to discover who responded with a particular value:
Host B Process 1: ?- listen(number(X), between(1, 5, X)). true. ?- Host A Process 1: ?- listen(number(X), between(7, 9, X)). true. ?- Host A Process 2: ?- listen(number(X), between(1, 5, X)). true. ?- bagof(X, broadcast_request(udp(peers,number(X):From,1)), Xs). From = ip(192, 168, 1, 103):34855, Xs = [7, 8, 9] ; From = ip(192, 168, 1, 103):56331, Xs = [1, 2, 3, 4, 5] ; From = ip(192, 168, 1, 104):3217, Xs = [1, 2, 3, 4, 5].
All incomming trafic is handled by a single thread with the alias
udp_inbound_proxy. This thread also performs the internal
dispatching using broadcast/1 and broadcast_request/1.
Future versions may provide for handling these requests in separate
While the implementation is mostly transparent, there are some important and subtle differences that must be taken into consideration:
udp_subnetscope is not reentrant. If a listener performs a broadcast_request/1 with UDP scope recursively, then disaster looms certain. This caveat does not apply to a UDP scoped broadcast/1, which can safely be performed from a listener context.
ip(A,B,C,D)or an atom or string of the format
A.B.C.D. Options processed:
For compatibility reasons Options may be the subnet mask.
|Address||has canonical form |
%prolog\n, followed by the Prolog term in quoted notation while ignoring operators. This hook may use alternative serialization such as fast_term_serialized/2, use
library(ssl)to realise encrypted messages, etc.
|Scope||is the scope for which the message is broadcasted. This can be used to use different serialization for different scopes.|
|Term||encapsulates the term broadcasted by
the application as follows:
udp(invalid_message)to stop processing the message.
This hook is intended to initiate a new node joining the network of
peers. We could in theory also omit the in-scope test and use a normal
broadcast to join. Using a different channal however provides a basic
level of security. A possibe implementation is below. The first fragment
is a hook added to the server, the second is a predicate added to a
client and the last initiates the request in the client. The excanged
join(X)) can be used to exchange a welcome handshake.
:- multifile udp_broadcast:udp_unicast_join_hook/3. udp_broadcast:udp_unicast_join_hook(Scope, From, join(welcome)) :- udp_peer_add(Scope, From),
join_request(Scope, Address, Reply) :- udp_peer_add(Scope, Address), broadcast_request(udp(Scope, join(X))).
?- join_request(myscope, "18.104.22.168":10001, Reply). Reply = welcome.