Skip to main content

Relay Peer to use JXTA with firewalls

6 replies [Last post]
danielfb
Offline
Joined: 2009-03-10

Dear All,

As I mentioned in another post I am quite new to the JXTA and the lack of documentation I am finding maybe will make you all laugh with my questions. Sorry.

It all began when we couldn't use any more RMI due to problems with firewalls in the client sites and we had to start looking for a solution. JXTA looked nice in the paper, but when it comes to the point of implementing it is quite tedious.

As a test I am trying to use the bidirectional pipe example to redirect to all the peer connected to it the message any of them sends from the standard input. It is working well without firewalls but when I start the firewall...

As my understood all we need to cross a firewall is to have a relay peer on the other side of the firewall. Ok, let's create a relay peer...

import java.io.File;
import java.net.InetAddress;

import net.jxta.id.IDFactory;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.platform.NetworkConfigurator;
import net.jxta.platform.NetworkManager;

public class RelayPeer {

public static final String Name = "RelaySeed";
public static final int TcpPort = 80;
public static final PeerID PID = IDFactory.newPeerID(PeerGroupID.defaultNetPeerGroupID, Name.getBytes());
public static final File ConfigurationFile = new File("." + System.getProperty("file.separator") + Name);

public RelayPeer() {
try {

// Creation of network manager
NetworkManager MyNetworkManager = new NetworkManager(NetworkManager.ConfigMode.RELAY,
Name, ConfigurationFile.toURI());

// Retrieving the network configurator
NetworkConfigurator MyNetworkConfigurator = MyNetworkManager.getConfigurator();

// Setting Configuration
MyNetworkConfigurator.setTcpPort(TcpPort);
MyNetworkConfigurator.setTcpEnabled(true);
MyNetworkConfigurator.setTcpIncoming(true);
MyNetworkConfigurator.setTcpOutgoing(true);

// Setting the Peer ID
MyNetworkConfigurator.setPeerID(PID);

// Starting the JXTA network
PeerGroup netPeerGroup = MyNetworkManager.startNetwork();
while(true){try{Thread.sleep(1000);}catch(Exception ex){}}
}catch(Exception ex) {
ex.printStackTrace();
}
}

public static void main(String[] arg) {
new RelayPeer();
}
}

As you see after the creation of it there's a while(true)... it's the only way I can think to not make the JVM stop and keep the peer 'alive'. I read somewhere one JVM cannot run more than one peer at a time. Please correct me if I am wrong... As this relay peer is supposed to be accessed from outside of the firewall I force it to use port 80. Is it right?

Message was edited by: danielfb

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
nicolasnn
Offline
Joined: 2010-01-18

Hello everyone,

I just started with JXTA, and I need to send messages between 2 computers. I've watched the JXTA programming guide, and I'm looking at the example of the pipes. How can I make the messages come from one computer to another if they are in different LAN?. I've been reading some post, and I know I need a Relay Peer, but where I put it? I need a third PC in which it is located?
Both computers have dynamic IP, and not like sending the message from one computer to another.

Thank you.

adamman71
Offline
Joined: 2007-01-31

Hi Nicolas and Daniel,

Since I took over 2.6, some tests have revealed major connectivity issue for peers located behind NATs in 2.5. These issues have been corrected in the 2.6 beta 2 release. So I advise you to try your application with this latest release.

Typically, you should have one Relay with a public address and set it as a seed for the other peers. Then things should be fine. Let me know if not.

Cheers,

J.

enygma2002
Offline
Joined: 2008-12-22

Hi!

To set up a relay peer, you need to have it start in RELAY or RENDEZVOUS_RELAY mode:
- either when you create a NetworkManager and pass the mode to the constructor
- or either by doing networkManager.setMode(ConfigMode.RELAY)

This relay peer needs to have a "real ip" or at least one forwarded port by which it can accept incomming connections from the outside world, without being blocked by a firewall or a NAT or other such problems.

Let's say that the address of this relay peer is 123.123.123.123:80 (it is recommended that you use a commonly opened port, so that the connecting peers don`t have problems themselves such as blocked outgoing traffic on weird ports, etc).

The relay peer can use either HTTP or TCP as the transport protocol. (although the documentation suggests that everything runs on HTTP in relay mode, in practice I have found that both TCP and HTTP work for a relay).

The connecting peers:

They can be normal, EDGE peers, but you need to set :
networkManager.getConfigurator().addSeedRelay("tcp://123.123.123.123:80");
in order for them to use your previously configured relay peer to relay communication past firewalls or NATs.
Also, because your peers can not accept incoming connections (because of the firewall problems), you also have to set:
networkManager.getConfigurator().setUseTCPIncoming(false);
networkManager.getConfigurator().setUseHTTPIncoming(false);
to tell the relay peers not to send back the information to you (because you are unreachable), instead to wait for you to reach them and get your info (jxta does this automagically in the background).

The rest of the communication is done normally, jxta will connect to the relay if it can not directly contact the destination peers.

In practice I only had success using relays when the Rendezvous of the network was a Relay as well, so it was set to RENDEZVOUS_RELAY mode and the connecting peers had to do something like:
networkManager.getConfigurator().addSeedRdv("tcp://123.123.123.123:80");
networkManager.getConfigurator().addSeedRelay("tcp://123.123.123.123:80");

I am sure that you can use relay peers as an addition to your network as well, not just as vital parts of your network (rendezvous as well).

I hope this helped you.

Good luck.

P.S.: Please try to ask more specific questions next time otherwise people won't bother to give you a full tutorial when the net is full of them. (ok, for jxta, a lot are outdated, but the logics is the same)

danielfb
Offline
Joined: 2009-03-10

Now my idea is I create a 'normal' peer, that will be a server for the peers that need to connect from outside of the firewall. This server I suppose as is in the same side of the relay peer doesn't need to add it as seed... correct me again if I am wrong... this is the server: it just creates the JxtaServerPipe and listens for connections... this is the server:

Message was edited by: danielfb

danielfb
Offline
Joined: 2009-03-10

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.logging.Logging;
import net.jxta.peergroup.PeerGroup;
import net.jxta.pipe.PipeID;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.util.JxtaBiDiPipe;
import net.jxta.util.JxtaServerPipe;

/**
* This is the server (receiver) side of the Bi-directional Pipe Tutorial.
*

* This example does the following :
*

    *
  1. Open a server pipe.
  2. *

  3. Listen for connect requests via {@code accept()}.
  4. *

  5. For each connect request spawn a thread which:
    *
      *
    1. Sends {@code ITERATIONS} messages to the connection.
    2. *

    3. Waits {@code ITERATIONS} responses.
    4. *

  6. *

*/
public class JxtaServerPipeExample {

/**
* Logger.
*/
private final static transient Logger LOG = Logger.getLogger(JxtaServerPipeExample.class.getName());

/**
* Connection count.
*/
private final static AtomicInteger connection_count = new AtomicInteger(0);

/**
* Number of messages to send
*/
final static int ITERATIONS = 1000;

final static String MESSAGE_NAMESPACE_NAME = "bidi_tutorial";
final static String MESSAGE_ELEMENT_NAME = "response";

private final static PipeID BIDI_TUTORIAL_PIPEID = PipeID.create(URI.create("urn:jxta:uuid-59616261646162614E504720503250338944BCED387C4A2BBD8E9411B78C284104"));

/**
* Gets the pipeAdvertisement attribute of the JxtaServerPipeExample class
*
* @return The pipeAdvertisement
*/
public static PipeAdvertisement getPipeAdvertisement() {
PipeAdvertisement advertisement = (PipeAdvertisement)
AdvertisementFactory.newAdvertisement(PipeAdvertisement.getAdvertisementType());

advertisement.setPipeID(BIDI_TUTORIAL_PIPEID);
advertisement.setType(PipeService.UnicastType);
advertisement.setName("JxtaBiDiPipe tutorial");

return advertisement;
}

/**
* Connection wrapper. Once started, it sends ITERATIONS messages and
* receives a response from the initiator for each message.
*/
private static class ConnectionHandler implements PipeMsgListener {
private final JxtaBiDiPipe pipe;

/**
* Constructor for the ConnectionHandler object
*
* @param pipe message pipe
*/
ConnectionHandler(JxtaBiDiPipe pipe) {
this.pipe = pipe;
pipe.setMessageListener(this);
}

/**
* {@inheritDoc}
*/
public void pipeMsgEvent(PipeMsgEvent event) {

try {

for(JxtaBiDiPipe bipipe : connectionList) {
if(bipipe!=pipe) {
bipipe.sendMessage(event.getMessage());
}
}

// System.out.println("Got Message :" + msgElement.toString());
} catch (Exception e) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] Failure during message receipt.", e);
}
}
}

}

static private List connectionList = new ArrayList();

/**
* main
*
* @param args command line args
*/
public static void main(String args[]) {
try {
final File home = new File(new File(".cache"), "server");
NetworkManager manager = new NetworkManager(NetworkManager.ConfigMode.ADHOC, "JxtaServerPipeExample", home.toURI());
manager.startNetwork();

PeerGroup netPeerGroup = manager.getNetPeerGroup();

PipeAdvertisement serverPipeAdv = JxtaServerPipeExample.getPipeAdvertisement();
JxtaServerPipe serverPipe = new JxtaServerPipe(netPeerGroup, serverPipeAdv);

// block forever until a connection is accepted
serverPipe.setPipeTimeout(0);

System.out.println("Waiting for JxtaBidiPipe connections on JxtaServerPipe : " + serverPipeAdv.getPipeID());
while (true) {
JxtaBiDiPipe bipipe = serverPipe.accept();
if (bipipe != null) {
System.out.println("JxtaBidiPipe accepted from " + bipipe.getRemotePeerAdvertisement().getPeerID() + " : " + bipipe.getRemotePipeAdvertisement().getPipeID() + " sending " + ITERATIONS + " messages.");
// Send messages
ConnectionHandler ch = new ConnectionHandler(bipipe);
connectionList.add(bipipe);

}
}
} catch (Throwable all) {
LOG.log(Level.SEVERE,"Failure opening server pipe.", all);
System.exit(-1);
}
}
}

Then we need a series of client peers that as I read somewhere, because they are on the other side of the firewall need to add the relay seed behind the firewall as a seed... here it's a bit confusing as I see two methods to add relay seeds:
MyNetworkConfigurator.addSeedRelay
and
MyNetworkConfigurator.addRelaySeedingURI(
but it doesn't matter whatever I do with the client I get the same error in the server when I start this client:

Mar 12, 2009 3:46:03 PM net.jxta.impl.endpoint.mcast.McastTransport run
SEVERE: failure during multicast receive
java.net.SocketException: socket closed
at java.net.PlainDatagramSocketImpl.receive0(Native Method)
at java.net.PlainDatagramSocketImpl.receive(Unknown Source)
at java.net.DatagramSocket.receive(Unknown Source)
at net.jxta.impl.endpoint.mcast.McastTransport.run(McastTransport.java:5
79)
at java.lang.Thread.run(Unknown Source)

And this is the client:

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.id.IDFactory;
import net.jxta.logging.Logging;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.pipe.PipeID;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.platform.NetworkConfigurator;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.util.JxtaBiDiPipe;

/**
* This is the client (initiator) side of the Bi-directional Pipe Tutorial.
*

* This example does the following :
*

    *
  1. Start {@code PIPE_CONNECTIONS} threads.
    *
  2. For each thread:
      *
    1. Open a connection to the server pipe.
    2. *

    3. Wait for messages sent by the server.
    4. *

    5. Send a response for each message received.
    6. *

  3. *

*/
public class JxtaExample implements Runnable, PipeMsgListener {
/**
* Logger
*/

private final static Logger LOG = Logger.getLogger(JxtaExample.class.getName());
/**
* The location of the JXTA cache directory.
*/
private final static File home = new File(new File(".cache"), "client");
/**
* The number of pipe connections we will establish with the server.
*/
private final static int PIPE_CONNECTIONS = 10;

/**
* The peer group context in which we are working.
*/
private final PeerGroup peergroup;
/**
* The per connection bi-directional pipe instance.
*/
private JxtaBiDiPipe pipe = null;
/**
* Per connection count of messages we have received.
*/
private final AtomicInteger received_count = new AtomicInteger(0);

final static String MESSAGE_NAMESPACE_NAME = "bidi_tutorial";
final static String MESSAGE_ELEMENT_NAME = "response";
final static String Name = "Test";
public static final PeerID PID = IDFactory.newPeerID(PeerGroupID.defaultNetPeerGroupID, Name.getBytes());

private final static PipeID BIDI_TUTORIAL_PIPEID = PipeID.create(URI.create("urn:jxta:uuid-59616261646162614E504720503250338944BCED387C4A2BBD8E9411B78C284104"));
/**
* Standard constructor.
*
* @param peergroup
*/
private JxtaExample(PeerGroup peergroup) {
this.peergroup = peergroup;
}

/**
* Send responses
*/
private void sendResponses() {
try{

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s;

while ((s = in.readLine()) != null && s.length() != 0 && pipe.isBound()) {

// An empty line or Ctrl-Z terminates the program

// Build the response and send it.
Message response = new Message();
MessageElement respElement = new StringMessageElement(MESSAGE_ELEMENT_NAME, s, null);
response.addMessageElement(MESSAGE_NAMESPACE_NAME, respElement);

try {
pipe.sendMessage(response);
} catch (IOException failure) {
failure.printStackTrace();
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] Failed sending a response message", failure);
}
return;
}
}
pipe.close();
manager.stopNetwork();

System.out.println("JXTA Shutdown");
}catch(Exception ex) {
ex.printStackTrace();
}
}

/**
* Called when a message is received for our pipe. Be aware that this may
* be called concurrently on several threads simultaneously. Since we are
* sending a response, which may block, it is important that we do not
* synchronize on this method.
*
* @param event message event
*/
public void pipeMsgEvent(PipeMsgEvent event) {

Message msg = event.getMessage();
if (msg == null) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.warning("[" + Thread.currentThread().getName() + "] Received an empty message, returning");
}
return;
}

try {
if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) {
LOG.finer("[" + Thread.currentThread().getName() + "] Received a message");
}
// Get the message element.
MessageElement msgElement = msg.getMessageElement(MESSAGE_NAMESPACE_NAME, MESSAGE_ELEMENT_NAME);

if (null == msgElement) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Missing message element");
}
return;
}

// Get message
if (msgElement.toString() == null) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.warning("[" + Thread.currentThread().getName() + "] null message received.");
}
return;
}
else {
System.out.println("---<"+msgElement.toString());
}

// Note that we received a message
synchronized (received_count) {
received_count.incrementAndGet();
received_count.notify();
}
} catch (Exception failure) {
if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
LOG.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Failure receiving event", failure);
}
}
}
public static PipeAdvertisement getPipeAdvertisement() {
PipeAdvertisement advertisement = (PipeAdvertisement)
AdvertisementFactory.newAdvertisement(PipeAdvertisement.getAdvertisementType());

advertisement.setPipeID(BIDI_TUTORIAL_PIPEID);
advertisement.setType(PipeService.UnicastType);
advertisement.setName("JxtaBiDiPipe tutorial");

return advertisement;
}
/**
* Set up this pipe connection and wait until the expected messages have
* been received.
*/
public void run() {
try {
PipeAdvertisement connect_pipe = getPipeAdvertisement();

System.out.println("[" + Thread.currentThread().getName() + "] Attempting to establish a connection to : " + connect_pipe.getPipeID());
pipe = new JxtaBiDiPipe(peergroup, connect_pipe, 5000, this, true);
System.out.println("[" + Thread.currentThread().getName() + "] JxtaBiDiPipe pipe created");
pipe.setMessageListener(this);
// We registered ourself as the msg listener for the pipe. We now
// just need to wait until the transmission is finished.
sendResponses();

System.out.println("[" + Thread.currentThread().getName() + "] Done!");
} catch (IOException failure) {
if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
LOG.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Failure opening pipe", failure);
}
} finally {

}
}
static NetworkManager manager;
/**
* main
*
* @param args command line args
*/
public static void main(String args[]) {
try {
// System.setProperty(Logging.JXTA_LOGGING_PROPERTY, Level.OFF.toString());

manager = new NetworkManager(NetworkManager.ConfigMode.ADHOC, "JxtaExample", home.toURI());
String seedName = "tcp://10.1.3.3:80";

NetworkConfigurator MyNetworkConfigurator = manager.getConfigurator();
URI LocalSeedingRendezVousURI = URI.create(seedName);
MyNetworkConfigurator.addRelaySeedingURI(LocalSeedingRendezVousURI);
MyNetworkConfigurator.setTcpPort(1234);
MyNetworkConfigurator.setTcpEnabled(true);
MyNetworkConfigurator.setTcpIncoming(true);
MyNetworkConfigurator.setTcpOutgoing(true);

// Setting the Peer ID
MyNetworkConfigurator.setPeerID(PID);
// Start JXTA
manager.startNetwork();
// manager.login("principal", "password");
System.out.println("yes?");
boolean waitForRendezvous = Boolean.valueOf(System.getProperty("RDVWAIT", "false"));
if (waitForRendezvous) {
// wait until a connection to a rendezvous is established
manager.waitForRendezvousConnection(0);
}

PeerGroup netPeerGroup = manager.getNetPeerGroup();
System.out.println("JXTA Started : " + netPeerGroup);

// Create PIPE_CONNECTIONS threads to connect to the server pipe.

Thread thread = new Thread(new JxtaExample(netPeerGroup), "Connection test");
thread.start();

// Stop JXTA

} catch (Throwable all) {
LOG.log(Level.SEVERE, "Failure starting bi-directional pipes.", all);
System.exit(-1);
}
}
}

This client server is working well without the firewalls, but I need the firewalls. Notice that the client is forced to use port 1234 MyNetworkConfigurator.setTcpPort(1234); but if I use 80 it's exactly the same...

Any help? Any ideas of what am I doing wrong or what way should I follow?

Thanks in advance,
Dani.

danielfb
Offline
Joined: 2009-03-10

Hi... how to post the code here formatted? It a mess when i try to post all my code.
Dani.

Dear All,

As I mentioned in another post I am quite new to the JXTA and the lack of documentation I am finding maybe will make you all laugh with my questions. Sorry.

It all began when we couldn't use any more RMI due to problems with firewalls in the client sites and we had to start looking for a solution. JXTA looked nice in the paper, but when it comes to the point of implementing it is quite tedious.

As a test I am trying to use the bidirectional pipe example to redirect to all the peer connected to it the message any of them sends from the standard input. It is working well without firewalls but when I start the firewall...

As my understood all we need to cross a firewall is to have a relay peer on the other side of the firewall. Ok, let's create a relay peer...

-----------------------------------------------------------------

As you see after the creation of it there's a while(true)... it's the only way I can think to not make the JVM stop and keep the peer 'alive'. I read somewhere one JVM cannot run more than one peer at a time. Please correct me if I am wrong... As this relay peer is supposed to be accessed from outside of the firewall I force it to use port 80. Is it right?

Now my idea is I create a 'normal' peer, that will be a server for the peers that need to connect from outside of the firewall. This server I suppose as is in the same side of the relay peer doesn't need to add it as seed... correct me again if I am wrong... this is the server: it just creates the JxtaServerPipe and listens for connections... this is the server:
------------------------------------------------------------------

Then we need a series of client peers that as I read somewhere, because they are on the other side of the firewall need to add the relay seed behind the firewall as a seed... here it's a bit confusing as I see two methods to add relay seeds:
MyNetworkConfigurator.addSeedRelay
and
MyNetworkConfigurator.addRelaySeedingURI(
but it doesn't matter whatever I do with the client I get the same error in the server when I start this client:

Mar 12, 2009 3:46:03 PM net.jxta.impl.endpoint.mcast.McastTransport run
SEVERE: failure during multicast receive
java.net.SocketException: socket closed
at java.net.PlainDatagramSocketImpl.receive0(Native Method)
at java.net.PlainDatagramSocketImpl.receive(Unknown Source)
at java.net.DatagramSocket.receive(Unknown Source)
at net.jxta.impl.endpoint.mcast.McastTransport.run(McastTransport.java:5
79)
at java.lang.Thread.run(Unknown Source)

And this is the client:
---------------------------------------------------

This client server is working well without the firewalls, but I need the firewalls. Notice that the client is forced to use port 1234 MyNetworkConfigurator.setTcpPort(1234); but if I use 80 it's exactly the same...

Any help? Any ideas of what am I doing wrong or what way should I follow?

Thanks in advance,
Dani.

Message was edited by: danielfb