API being programmed during my Ph.D. within the CASA research team at the UBS with the laboratory IRISA.
This project aims to offer a simplified and easy-to-use java API for the IBR-DTN (BP implementation). It communicates with the daemon to send and receive bundles while focusing on the ease of its usage.
The first thing you have to do is to create your own handler implementing the interface BundleHandler in order to override the onReceive(Bundle bundle)
method to process, as need be, the received bundles:
public class PrintingHandler implements BundleHandler {
@Override
public void onReceive(Bundle bundle) {
System.out.println("Received bundle:" + bundle.toString());
}
}
You can then create your MyBpApplication
instance and set the EID. IBR-DTN supports both group and singleton EID. If your local node name is dtn://localname and you want to receive bundles sent to the URI dtn://localname/MyApp then set the eid with the path-part of the URI, this way: bpApp.setEid("MyApp");
. However, if you want to receive bundles sent to the URI dtn://global/all set the EID with the full URI, not the path-part only. If the eid
contains "://" it is assumed that the application is not a singleton.
To send a bundle, just call the method send(Bundle bundle)
of BpApplication.
Here is a full example:
public static void main(String[] args) throws InterruptedException {
//My local node name is "dtn://actuator"
String eid = "app";
BpApplication bpApp = new BpApplication();
//You can also call the constructor as follow: new BpApplication(eid);
//and get rid of the next line
bpApp.setEid(eid); //Bundle sent to "dtn://actuator/app" will be received..
//Set your Handler
bpApp.setHandler(new PrintingHandler());//.. and processed by this handler.
Bundle bundle = new Bundle("dtn://logger/X", "Payload\n".getBytes());
//To add a payload block into the bundle use: boolean bundle.[addDecoded(byte[] data)|addEncoded(String data)]
//This will send the bundle from dtn://actuator/app to dtn://logger/X, with the payload "Payload\n".
bpApp.send(bundle);
}
Added to sending and receving bundles, the command neighbor list
from the protocol extended
is also available. To perform this method, call List<String> BpApplication::getNeighborList()
.
The two main classes are Dispatcher and BpApplication. Dispatcher is the class that manages the communication with the daemon through the two communicators (input and output). It notifies received bundles to the BpApplication and sends bundles to the daemon on behalf of BpApplication. The CommunicatorInput, among other classes, sets the state of the Dispatcher. States are used so the Dispatcher does not send a bundle while fetching a received one. Dispatcher and BpApplication communicate using observable FifoBundleQueue, one for received bundles, the other one for the bundle to be sent.
See the architecture.svg to get a visual overview.
Bundles can have multiple payload blocks. To avoid huge bundle (that may be harmful for the bandwidth) a payload block is added only if it is the first payload block and if the new payload block weight summed to the current payload blocks weight is less than Api.MAX_PAYLOAD_WEIGHT
.
Very important too, the test done when a payload block is requested to be added is done too when bundle are received. This test can lead to refuse a payload block for received bundle (that may be bigger than Api.MAX_PAYLOAD_WEIGHT
). This is a bug, Bundle
should have a boolean set when it is a received bundle so the test is not performed in this case (and the Sender
should test it before sending the bundle).
Tests performed on a localhost with a i5-3570 CPU @ 3.40GHz, running Debian 3.2.68-1+deb7u2, IBR-DTN daemon 0.12.1 (build 7c220eb) and Java 1.7 (OpenJDK Runtime Environment (IcedTea 2.5.5) (7u79-2.5.5-1~deb7u1)).
With the code of 2562d61, 2000 bundles were sent before the app registered itself. The BundleHandler code didn't process bundles but just counted them and checked if 2000 were received to finally print System.currentTimeMillis()
once the last was received.
The average time of reception for the 2000 is 5030 ms (about 2.5 ms/each).
Note that this measurement include the delay of registration.
The average delay for an app to create the Java object and successfully register to the daemon is about 27 ms.
This code was first a POC of an easy-to-use IBR-DTN Java interface. The code has been made in a quick-and-dirty way. This include many lines with while(this.dispatcher.getState() != State.SOME_STATE);
. These active waits are CPU hungry, even though two while();
at most can run at the same time.
A quick-and-dirty (again, but well... A PhD Thesis is time-consuming, isn't it?) solution would be to add a Thread.Sleep(1);
. This would (partially) solve the CPU usage.
A cleaner solution to handle this would be to develop a state automate by using Java Future and state Listeners.
Here is the output of time
to receive 200 bundles using the dtnrecv
command-line tool, and this Java interface:
dtnrecv --count 200 --name test 0.02s user 0.01s system 44% cpu 0.054 total
#With the CPU hungry version:
java -jar ibr-dtn_java-api.jar 1.11s user 0.20s system 140% cpu 0.934 total
#With the *quick-and-dirty* solution (and a 1 ns sleep):
java -jar t-while_wait-1ns.jar 0.89s user 0.18s system 62% cpu 1.728 total
#With 10k bundle to receive:
time dtnrecv --count 10000 --name test 0.59s user 0.15s system 24% cpu 3.097 total
time java -jar 10000-exit_test-while_wait-1ns.jar 9.08s user 6.66s system 23% cpu 1:07.43 total
Using the Java interface is far slower, but the CPU usage is far more friendly than before. Again, this is not the best solution!
- Add executor (Thread poll) for outgoing bundles. Actually having a Threads for each bundles is useless ..
- Add executor (Thread poll) for incomming bundles. * .. as the work has to be done sequentially. The
ScheduledThreadPoolExecutor
works with only one thread.* - Set, clear and test single flags of bundle.
- Remove the
ScheduledThreadPoolExecutor
and create aExecutors.newSingleThreadExecutor
instead. - Test reception performance.
- Test registration performance.
- Test emission performance.
- Multi-payload blocks bundle (Api.MAX_PAYLOAD_BLOCK is the limit of the payload block number).
- Enable the
neighbor list
command. - 🐛 Solve the bug when an application is stopped, and started back.
- CPU usage friendly.