First, create a project and setup the pom.xml if you are using maven:
<repositories>
<repository>
<id>tomp2p.net</id>
<url>http://tomp2p.net/dev/mvn/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.tomp2p</groupId>
<artifactId>tomp2p-all</artifactId>
<version>5.0-Beta8</version>
</dependency>
</dependencies>
In order to create a P2P network with TomP2P, all peers need to:
In the following example, the application is a mapping service that maps a name to a value. So, first we create an identity, listen for incoming connections, and bootstrap to a well known peer in our constructor. The bootstrapping in this example is done via broadcasting, so both peers need to be reachable via layer 2. We assume that the well known peer listens on port 4001. The application can store and retrieve key and values. If you have 3 arguments in the main method, then a name and its value are stored, if you have 2 arguments, the key is queried. For simplicity, all operations are blocking.
import java.io.IOException;
import java.net.InetAddress;
import net.tomp2p.dht.FutureGet;
import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.futures.FutureBootstrap;
import net.tomp2p.p2p.PeerBuilder;
import net.tomp2p.peers.Number160;
import net.tomp2p.storage.Data;
public class ExampleSimple {
final private PeerDHT peer;
public ExampleSimple(int peerId) throws Exception {
peer = new PeerBuilderDHT(new PeerBuilder(Number160.createHash(peerId)).ports(4000 + peerId).start()).start();
FutureBootstrap fb = this.peer.peer().bootstrap().inetAddress(InetAddress.getByName("127.0.0.1")).ports(4001).start();
fb.awaitUninterruptibly();
if(fb.isSuccess()) {
peer.peer().discover().peerAddress(fb.bootstrapTo().iterator().next()).start().awaitUninterruptibly();
}
}
public static void main(String[] args) throws NumberFormatException, Exception {
ExampleSimple dns = new ExampleSimple(Integer.parseInt(args[0]));
if (args.length == 3) {
dns.store(args[1], args[2]);
}
if (args.length == 2) {
System.out.println("Name:" + args[1] + " IP:" + dns.get(args[1]));
}
}
private String get(String name) throws ClassNotFoundException, IOException {
FutureGet futureGet = peer.get(Number160.createHash(name)).start();
futureGet.awaitUninterruptibly();
if (futureGet.isSuccess()) {
return futureGet.dataMap().values().iterator().next().object().toString();
}
return "not found";
}
private void store(String name, String ip) throws IOException {
peer.put(Number160.createHash(name)).data(new Data(ip)).start().awaitUninterruptibly();
}
}
To run this example, you first have to start the well known peer on port 4001:
java ExampleSimple 1 test.me 192.168.1.1
Then you can add as many other clients as you want:
java ExampleSimple 2 test.me
The output should look something like
Name:test.me IP:192.168.1.1
First, a peer with an ID has to be created. You can either set a random peer ID, or you can create the peer with a KeyPair
, which takes a public key and generates the ID (SHA-1) out of this key. In addition you can add various parameters and configuration options to the PeerMaker class. The example below shows the creating of a peer with a random ID.
Random rnd = new Random();
Peer peer = new PeerBuilder(new Number160(rnd)).ports(4001).start();
The next example shows the creating of a peer with a public / private key.
KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA");
KeyPair pair1 = gen.generateKeyPair();
Peer peer = new PeerBuilder(pair1).ports(4001).start();
Since its a P2P network, we need some more peers. You can either create a new peer and listen to another port, or you can attach a new peer to an existing port. The later is more resource friendly and many thousands of peers can be created.
Peer another = new PeerBuilder(new Number160(rnd)).masterPeer(peer).ports(4002).start();
Before we bootstrap, we need to discover if our peer is behind a NAT and if TomP2P needs to configure NAT via UPNP
FutureDiscover future = another.discover().peerAddress(peer.peerAddress()).start();
future.awaitUninterruptibly();
The next step in a P2P network is to bootstrap. Lets bootstrap another
to peer
. For now we use awaitUninterruptibly
to wait for the result. The future concept is explained in the next section.
FutureBootstrap future = another.bootstrap().setPeerAddress(peer.getPeerAddress()).start();
future.awaitUninterruptibly();
In order to store data in TomP2P, the object needs to be wrapped with the Data
class. The data
class offers additional features, such as setting a TTL or signing the object. Then, put
or add
is called, which starts the routing process, finds the peers close to nr
, where the data is stored. Since we “only” have a Peer object, we need to create a PeerDHT object first.
PeerDHT pdht = new PeerBuilderDHT(another).start();
Data data = new Data("test");
Number160 nr = new Number160(rnd);
FuturePut futurePut = pdht.put(nr).data(data).start();
futurePut.awaitUninterruptibly();
A proper shutdown is initiated by calling shutdown()
from the master peer. This also returns a future object, and if you want to wait until the shutdown is finished, you have to call
peer.shutdown();
//or peer.shutdown().awaitUninterruptibly();
Since TomP2P uses non-blocking communication, a future object is used to keep track of future results. Thus, a get().start()
, put().start()
, or add().start()
returns immediately and the future object is used to get the results from those operations. Most of the time the following code will not work as expected, since the get().start()
returns immediately:
FutureGet futureGet = pdht.get(nr).start();
//you need to call futureDHT.awaitUninterruptibly() to get any data;
futureGet.data();
There are two options to get the data from the future object. The first is by blocking and waiting for the result to arrive, which can be either await()
or awaitUninterruptibly()
. The second option is to add a listener, which gets called whenever a result is ready. It is preferred to use this second option and avoid blocking, because in the worst case, you might cause a deadlock if await()
is called from a wrong (I/O) thread. If such a listener is used, then the listeners gets called in all cases. If no peer replies, the timeout handler triggers the listener.
futureGet.addListener(new BaseFutureAdapter<FutureGet>() {
@Override
public void operationComplete(FutureGet future) throws Exception {
if(future.isSuccess()) { // this flag indicates if the future was successful
System.out.println("success");
} else {
System.out.println("failure");
}
}
});
Under the hood, TomP2P uses the future concept in many places, for example the routing process is entirely based on futures objects and listeners.
Most of the examlpes run on the same host to make it easier to test them. Since TomP2P is meant to run on many hosts, the following examples show how to set up TomP2P on multiple hosts. These examples cover the setup of the peers, since the operations such as put()
an get()
do not change.
Random rnd = new Random();
Bindings b = new Bindings();
b.addInterface("eth0");
// create a peer with a random peerID, on port 4000, listening to the interface eth0
Peer peer = new PeerBuilder(new Number160(rnd)).bindings(b).ports(4001).start();
This snippet creates a peer and listens on the interface eth0
. If b.addInterface("eth0");
is ommited, the peer listens to all interfaces. Lets assume this snippet runs on host A
with IP address 192.168.1.10 and on host B
with IP address 192.168.1.20, then the following snippet connects host A
to host B
.
InetAddress address = Inet4Address.getByName("192.168.1.20");
FutureDiscover futureDiscover = peer.discover().inetAddress( address ).ports( 4000 ).start();
futureDiscover.awaitUninterruptibly();
FutureBootstrap futureBootstrap = peer.bootstrap().inetAddress( address ).ports( 4000 ).start();
futureBootstrap.awaitUninterruptibly();
For more information about setBehindFirewall(true)
, please read UPNP NAT and Port Forwarding detection in the advanced topics.