In this step we build a very simple ping pong protocol with the following specification:
The protocol has two messages:
PingMessage
: sent by the initiator and carries a monotonically increasingcounter
and a stringmessage
PongMessage
: sent by the responder and carries a monotonically increasingcounter
and a stringmessage
The protocol has one timer:
NextPingTimer
: used to trigger the next ping message
The protocol handles may process the following channel events:
OutConnectionUp
: when an outgoing connection is establishedOutConnectionFailed
: when an outgoing connection failsOutConnectionDown
: when an outgoing connection is closedInConnectionUp
: when an incoming connection is establishedInConnectionDown
: when an incoming connection is closed
The protocol has the following parameters:
n_pings
: the number of ping messages to send, if not specified or is set to0
, the protocol will only wait forPingMessage
messagesping_interval
: the period between ping messagestarget_address
: the address of the target nodetarget_port
: the port of the target node
The protocol works as follows:
- The protocol will check if
n_pings
is set to higher than0
. If it is, the protocol will open a connection to the target host. - When the
OutConnectionUp
event is received, the protocol will start theNextPingTimer
. - When the
NextPingTimer
triggers, the protocol will send aPingMessage
to the target host. - When the protocol receives a
PingMessage
it will send aPongMessage
to the host that sent thePingMessage
. - When the protocol receives a
PongMessage
it will print the message to the console. - The protocol will stop sending
PingMessage
messages when it has sentn_pings
messages. - When the protocol has sent
n_pings
messages, it will close the connection to the target host.
Use the following abstractions to implement the protocol:
Babel offers simple and generic networking and timer abstractions. These come in the form of abstract Java classes and API functions.
Networking abstractions allow developers to send and receive network messages. This enables developers to implement network protocols without having to deal with the underlying network stack.
Timer abstraction allows developers to schedule events to trigger after a given delay. These are useful to implement protocols that require periodic actions or need to handle timeouts.
Networking abstractions include:
- Network
channel
abstractions, that provide an interface to the operating system network stack.ProtoMessage
abstract Java class, that defines a Babel network message type. sendMessage
API calls, that deliver a network message to thechannel
abstraction to be sent to a different process.
You need to create a channel to be able to send message to the network. You can do this as follows:
Properties channelProps = new Properties();
channelProps.setProperty(TCPChannel.ADDRESS_KEY, "127.0.0.1");
channelProps.setProperty(TCPChannel.PORT_KEY, "9000");
// create the channel with the provided properties
// createChannel is a function defined in the GenericProtocol class
channelId = createChannel(TCPChannel.NAME, channelProps);
You need to define a Protocol Messages for your protocol. You do this by extending the ProtoMessage class:
import pt.unl.fct.di.novasys.babel.generic.ProtoMessage;
public class MyProtocolMessage extends ProtoMessage {
public static final short MSG_CODE = 1; // unique code of the message
// protocol message fields
public MyProtocolMessage() {
super(MSG_CODE);
}
// protocol message logic
// protocol message serialization/deserialization
public static ISerializer<? extends ProtoMessage> serializer = new ISerializer<MyProtocolMessage>() {
public void serialize(MyProtocolMessage msg, ByteBuf out) {
// serialize the message
// out is a Netty ByteBuf where the message should be serialized
}
public PingMessage deserialize(ByteBuf in) {
// deserialize the message
// in is a Netty ByteBuf from where the message should be deserialized
}
};
}
To send the message, just use the sendMessage
API call, as follows:
// define a Host destination object
destination = new Host(Inet4Address.getByName("127.0.0.1"), 9001);
// send the message
sendMessage(channelId, new MyProtocolMessage(), destination);
Timer abstraction include:
ProtoTimer
abstract Java class, that defines a Babel timer event type.setupTimer
API calls, that prepare a timer event to trigger after a given delay.setupPeriodTimer
API calls, that prepare a timer event to trigger periodically after a given delay.cancelTimer
API calls, that cancel a previously setup timer event.
To define the timer event, you need to extend the ProtoTimer class:
import pt.unl.fct.di.novasys.babel.generic.ProtoTimer;
public class MyProtocolTimer extends ProtoTimer {
public static final short TIMER_CODE = 1; // unique code of the timer
// protocol timer fields
public MyProtocolTimer() {
super(TIMER_CODE);
}
// protocol timer logic
@Override
public ProtoTimer clone() {
return this;
}
}
To setup a timer, just use the setupTimer
API call, as follows:
// setup a timer to trigger after 1000 milliseconds
// returns the id of the timer event
long timerId = setupTimer(new MyProtocolTimer(), 1000);
To setup a periodic timer, just use the setupPeriodTimer
API call, as follows:
// setup a timer to trigger after 1000 milliseconds and then every 500 milliseconds
// returns the id of the timer event
long timerId = setupPeriodTimer(new MyProtocolTimer(), 1000, 500);
To cancel a timer, just use the cancelTimer
API call, as follows:
// cancel the timer event with the given id
cancelTimer(timerId);
Implement the PingPong protocol.
import pingpong.timers.NextPingTimer;
import pt.unl.fct.di.novasys.babel.core.GenericProtocol;
import pt.unl.fct.di.novasys.channel.tcp.events.OutConnectionUp;
public class PingPongProtocol extends GenericProtocol {
public PingPongProtocol() {
super("PingPong", (short) 1);
}
public void init(Properties props) {
// implement the initialization of the protocol
// need to register handlers for events, timers and messages
}
// implement protocol handlers
private void uponNextPing(NextPingTimer timer, long timerId) {
// what to do when the timer "NextPingTimer" triggers
}
private void uponReceivePingMessage(PingMessage msg, Host from, short sourceProto, int channelId) {
// what to do when a "PingMessage" is received
}
private void uponReceivePongMessage(PongMessage msg, Host from, short sourceProto, int channelId) {
// what to do when a "PongMessage" is received
}
// channel events
private void uponOutConnectionUp(OutConnectionUp event, int channelId) {
// what to do when an "OutConnectionUp" event is received
}
private void uponOutConnectionFailed(OutConnectionFailed event, int channelId) {
// what to do when an "OutConnectionFailed" event is received
}
private void uponOutConnectionDown(OutConnectionDown event, int channelId) {
// what to do when an "OutConnectionDown" event is received
}
private void uponInConnectionUp(InConnectionUp event, int channelId) {
// what to do when an "InConnectionUp" event is received
}
private void uponInConnectionDown(InConnectionDown event, int channelId) {
// what to do when an "InConnectionDown" event is received
}
}
The main class should look like this:
import ...
public class Main {
private static final Logger logger = LogManager.getLogger(Main.class); // log4j logger
public static void main(String[] args) throws Exception {
//Creates a new instance of babel
Babel babel = Babel.getInstance();
//Reads arguments from the command line and loads them into a Properties object
Properties props = Babel.loadConfig(args, null);
//Creates a new instance of the PingPongProtocol
PingPongProtocol pingPong = new PingPongProtocol();
//Registers the protocol in babel
babel.registerProtocol(pingPong);
//Initializes the protocol
pingPong.init(props);
//Starts babel
babel.start();
}
}
To compile your code, run the following commands:
mvn clean package
docker build -t babel-tutorial/a-pingpong .
You need to create a docker network for the tutorial:
docker network create babel-tutorial-net
To remove the network:
docker network rm babel-tutorial-net
The protocol accepts the following arguments:
interface
: the network interface to use (e.g., eth0)target_address
: the address of the target host (e.g., ping-server)n_pings
: the number of pings to send (e.g., 1)message
: the message to send (e.g., hello)ping_interval
: the period between pings (in milliseconds)
To run the protocol, you need to run the following commands:
This will run the protocol in server mode:
docker run --network babel-tutorial-net --rm -h ping-server --name ping-server -it babel-tutorial/a-pingpong interface=eth0
This will run the protocol in client mode:
docker run --network babel-tutorial-net --rm -h ping-client --name ping-client -it babel-tutorial/a-pingpong interface=eth0 target_address=ping-server n_pings=1 message=hello