Java rmi over the internet - java

I am developing a game in Java using RMI for all network communication. RMI allows me to call method on my server, but it is not enough for me. I also want the server to be able to spread message among connected clients.
My clients look up for the server (it's interface extends Remote) and register on it. It allows the server to know who is connected. My clients also implement an interface that extends Remote. Here is some part of my code:
Interfaces declaration:
public interface IServer extends Remote {
void connect(IClient Client) throws RemoteException, ExistingItemException;
//...
}
public interface IClient extends Remote {
public void notify(Notification Notification) throws RemoteException;
//...
}
Server side:
//int RMIPort = 1099, ServerPort = 1100;
IServer Server = new RMIServer();
IServer Proxy = (IServer) UnicastRemoteObject.exportObject(Server, ServerPort);
LocateRegistry.createRegistry(RMIPort).rebind("//" + LocalIP + ":" +
RMIPort + "/xxx", Proxy);
Client side:
//Sets the local reference to IServer and creates the IClient
setInstance(new Client(Login, (IServer) LocateRegistry.getRegistry(RemoteIP).
lookup("//" + RemoteIP + ":" + RMIPort + "/xxx")));
//Gets the IClient and makes it available for the IServer to call notify(...)
Proxy.connect((IClient) (UnicastRemoteObject.exportObject(getInstance(), 0)));
This solution works in local but doesn't when I'm trying to use it through Internet.
I have setup my router to expose my computer to a fix IP address and forward the 1099 and 1100 ports. This solution allow other developers to "lookup" my server and get a valid and usable Proxy, but it doesn't allow them to export their IClient. It seems that the JVM tries to export the object to their local network and the execution stops here.
So, I have tried to pass -Djava.rmi.server.hostname=my-external-IP JVM argument both local for my server and remote for clients. Doing so, exportObject throws a ConnectException to the remote ip instead of local one.

Forget it. Using callbacks over the Internet requires every client to configure his firewall/NAT box/whatever to allow inbound connections and probable port forwarding. Many of them aren't configurable at all, and many of them are run by net admins who just won't do it.
NB you would also have to export on a fixed port.

There is an alternative:
http://dev.root1.de/projects/simon/wiki#Why-SIMON-is-better-than-
The protocol is not compatible to RMI, but the usage is almost the same. Changing from RMI to SIMON is typically not that hard. I heared from users that switching to SIMON took just 30min.

You might think about alternative solution than using RMI, because I believe it was designed for Intranet applications (i.e. Enterprise application within a local network) not Internet.
I would suggest using a long-running TCP connection between the client and the server so at any moment the server wants to communicate back it just pushes a message to this connection.
In this case, initiating the connection will always be the client task. It is very similar to how mail clients connects to imap or pop3 server.

Related

Bind address and port to RMI

I'm developing a chat, and firstly I was using sockets and streams to communicate between the server and client, which I already tested with my friends and it worked. Now I`m trying to convert to RMI.
So, it is working locally, but when my friend tries to connect he gets a refused connection. Weirder is that it shows the server local ip (192.168.25.28).
My code looks like this:
public void conectar() throws RemoteException, MalformedURLException, NotBoundException{
// COMUNICADOR SERVIDOR (USADO PELO CLIENTE)
comunicadorServidor = new ComunicadorServidor(id); // cria um novo comunicador
LocateRegistry.createRegistry(8081); // inicia o registro RMI na porta 8081
Naming.rebind("//:8081/ComunicadorServidor", comunicadorServidor); // vincula o objeto comunicador ao endereço RMI
// COMUNICADOR CLIENTE (USADO PELO SERVIDOR)
comunicador = (IComunicadorCliente) Naming.lookup("//" + conexao.getInetAddress().getHostAddress() + ":8082/ComunicadorCliente"); // procura o comunicador no cliente
}
So I use RMI in both server and client, and one connect to each another, because in server I have methods like Authentication and Send Message, which the cliente uses, and in the client I have methods like Update Users List and Receive Message, which the server uses.
So first I create an instance of the RMI class (server-side), create a registry on port 8081, then bind the object to port 8081 with the name "ComunicadorServidor".
And after that the server tries to connect to the client RMI.
So I read about the rebind method, and if I don't specify an address, it will bind to the local host address, which I ask: even if it's binding to my local machine, is it still available outside? If not, how can I bind it in the same away as socket, where I just specify some port and it will be available to any address, like localhost, local ip, or external ip.
And about the lookup, what is the best way to get the client IP (right now I'm taking from the socket), how can I be sure that I will get a correct IP? Can I connect to the client RMI without a socket?
And in the client side it's essentialy the same thing, except it binds the RMI class to 8082 and lookup for the RMI class in server-side with the provided IP and port.
Weirder is that it shows the server local ip (192.168.25.28).
That's correct. If you need it to appear on a public IP address you need to look up java.rmi.server.hostname, and see Item A.1 in the RMI FAQ.
So I read about the rebind method, and if I don't specify an address, it will bind to the local host address, which I ask: even if it's binding to my local machine, is it still available outside?
The Registry is available to the outside world if the server-side firewall rules so permit. Note that bind in the RMI sense just means associating a remote object with a name in the Registry: it has nothing to do with Socket.bind().
And about the lookup, what is the best way to get the client IP (right now I'm taking from the socket), how can I be sure that I will get a correct IP? Can I connect to the client RMI without a socket?
You can't 'connect' in RMI at all, but you don't need the client's IP address. The client should register its remote object with the server via a server remote method, e.g. register(Client client) throws RemoteException. Then the server has a stub on which it can call client remote methods directly (client-side firewall permitting).

Should i use the same port numbers when sending data through UDP?

When we send data (in this case) to a client/server, does this really matter to use the same port number?
My guess is no, because it doesn't matter which port you are using when sending data to. (The protocol gives it to you randomly internally - this is the idea?) The only thing has to be kept, the port has to be any availabe one on the receiver machine(above 1000, because those are reserverd by the system), and if that receiver decides to send something back, he or she will have enough information about sender: his IP address, port number ect. As far as i know, a received packed provides with all of that info.
Below is just an illustration of what i've said above.
public class Server {
public static void main(String[] args) {
GameServer server = new GameSever(9822);
server.start();
InetAddress address = null;
int port = 7877;
try {
address = InetAddress.getByName("192.168.0.2");
} catch (UnknownHostException e) {
e.printStackTrace();
}
server.send(new byte[] { 1, 2, 3 }, address, port);
}
}
When a server listens on a computer, it specifies a port it wants it's connections coming in from , so ports are important for setting up servers. This is useful as you can have multiple applications listening on different ports without the different applications accidentally talking to eachother. So you should decide on a port that isn't a standard( 80 is for HTTP for example) to exclusively use for you gameserver so the client knows which port to send the requests to.
If you want to handle multiple connections at once the best thing to do is threading.
When we send data (in this case) to a client/server, does this really
matter to use the same port number? My guess is no, because it doesn't
matter which port you are using when sending data to.
Firstly, use the terms client and server distinguishly(as generally client initiates by sending the message, to which the server responds).
Next, the port which you're using is logically of no significance, the reason being server uses request.getPort() to determine the port while seding the response; (request is a DatagramPacket sent by the client).
Though you can hardcode the port at server(if known beforehand), but, it is a bad idea. What in those applications where you've no idea about who sent the datagram packet?
Java documentation by Oracle also uses an example of client-server where client's port number is left for the constructor to pick. It mentions :
Mostly, the client uses a constructor that does not require a port number. This constructor just binds the DatagramSocket to any available local port.
It doesn't matter what port the client is bound to because the DatagramPackets contain the addressing information. The server gets the port number from the DatagramPackets and send its response to that port.
MORE INFO (taken from Java Network Programming) :
public DatagramSocket() throws SocketException
This constructor creates a socket that is bound to an anonymous port. For example:
DatagramSocket client = new DatagramSocket();
Pick this constructor for a client that initiates a conversation with a server. In this scenario, you don’t care what port the socket is bound to because the server will send its response to the port from which the datagram originated. Letting the system assign a port means that you don’t have to worry about finding an unused port. If, for some reason, you need to know the local port, you can find out with the getLocalPort() method.
NOTE : The same socket can receive the datagrams that a server sends back to it(underlying implementation).
Whereas, the below constructor creates a socket that listens for incoming datagrams on a particular port, specified by the port argument :
public DatagramSocket(int port) throws SocketException
Use this constructor to write a server that listens on a well-known port.
Short answer:
Your guess is correct.
Longer answer:
Client gets a random port number when sending data to a server and each packet has a header which contains the info about client's port, ip address ect. So server can easily retreive this information and send anything back.
Whereas the server needs to be bind to a specific port in order to clients be able to send data to that server because when you use UDP, you need to specify an IP address and a port.

Java Socket: How to bind a specific local address to a Socket without a port number specified

I want to let the user have the freedom to choose a specific local address to connect to the target server since there might be customized routing policies, but want the program to pick up an ephemeral port instead of specifying one since that may need manual-test of local port availability.
I checked the constructors of "Socket" and "InetSocketAddress", it seems none of them have one to do the above task (even though it can pick up a local address and an ephemeral port simultaneously), and there is no method to do so after the initialization.
There is a construtor
public Socket(InetAddress address,
int port,
InetAddress localAddr,
int localPort)
throws IOException
that should be suitable for your requirement. If localPort is 0, the system will pick a free port.
You can create a Socket and call connect on it later.
Socket socket = new Socket(); // no idea where to connect
socket.connect(addressAndPort); // now I know.
What I do is have a TCPRegistry for testing purposes. This component takes care of aliased ports. e.g. host.port1. It gives it an ephemeral port on the server and allows the client to connect to it using the same string.
Note: to allow the client to start before the server I can ask it to pre-build these ServerSocket in the unit test.
Finally, at the end of the test, I can either check all Sockets and ServerSockets were closed, or forcefully clean them up.
It is designed for NIO, but could be adapted for plain IO TCPRegistry

Java.net.SocketException: Permission denied: connect

I have two PCs in one network that I want to connect. One of them should send a notification to the other via TCP. One the one PC I have a "server" (Python script) socket which waits for the "client"(Jar file) to send a specific String and then it gives me a notification. This works perfectly fine when I'm trying it out one one PC. But when I want to do the intended action the "client" PC's .jar gives me an error that the connection is refused. Do I have to open a specific port on the other PC or what else could cause trouble? One PC runs Fedora the other Windows 8
"Server Code"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 5005))
s.listen(1)
try:
while True:
komm, addr = s.accept()
while True:
data = komm.recv(1024)
if data == "$":
noty()
if not data:
komm.close()
break
finally:
s.close()
"Client" Code
public static void main(String[] args) throws Exception {
Socket socket = new Socket("192.168.178.25", 5005);
OutputStream out = socket.getOutputStream();
String dat = "$";
out.write(dat.getBytes());
socket.close();
}
Your server is probably binding to the wrong interface,
calling
s.bind(("", 5005))
Without setting an interface will allow the program to pick what ip address / interface it will connect to.
Since your client is trying to connect to ("192.168.178.25", 5005); you may want to put an IP address into the bind call to prevent the server picking the wrong ip interface.
Example:
s.bind(("192.168.178.25", 5005))
if its permission denied then something is blocking your connection with the computer. i would try to open a port and see if that works. if you want an example of java sockets you can take a look at my SUPER Tic-Tac-Toe Multiplayer it uses java sockets to send strings to the clients as a way to represent what actions the clients should take.

How to simulate server down/available in java?

Our application has server/client side. The client supports both offline and online work mode.
So I need to test the client when server down, regain connective.
Question comes. How to simulate server down. Use codes to switch from down to ready, or from ready to down state.
Thanks in advance.
Joseph
update:
Actually, I could not extend the server interface to response the incorrect status. In my test scenario, the server is transparent. So incorrect url + port is a solution to do this.
But I could not modify the url when the session is valid. Another method is modify the hosts file to do this. I have to face the privilege issue in Windows.
Depends on what you mean by "server down". Possible options are:
Write a fake/dummy server that can return error messages corresponding to being down for test purposes.
Change the IP address of the server that your client looks for to a non-existing one so that it will think that the server is entirely down.
The basic idea is to mock the behavior of your server somehow. You could use mocking frameworks to do so.
You could also create manual mocks for testing purposes. Let the "proxy" of the server on the client implement this interface:
public interface IServer
{
bool foo();
}
You could create a "fake" implementation of that server and return whatever you'd like
public class FakeOfflineServer implements IServer
{
public bool foo()
{
// throw some exception here.
}
}
This approach allows you to fake different scenarios (no network connectivity, invalid credentials, etc.)
You could also use composition to switch from up to down in your tests:
public bool FakeServer implements IServer
{
private IServer offline = new FakeOfflineServer();
private IServer online = new Server();
public bool isUp = false;
private IServer getServer()
{
return isUp ? online : offline;
}
public bool foo()
{
return getServer().foo();
}
}
While testing server down, give any incorrect URL OR Port (Prefered). For recovery give the correct URL/Port.
This depends where you are testing. If you're unit testing, the best option is the mocking suggested by Bryan Menard.
If you're testing in an integration or production environment, You can actually cut the connection between you and the server.
Depending upon your operating system, you can do this in a number of ways.
For Windows based systems, Fiddler is fantastic. You can simulate almost anything, including delays on the requests and indeed just throwing requests away. It does not require admin access for Windows.
For linux based systems, one technique I've used in the past is to use a proxy server, or cut the port at operating system level. You can do this using iptables for instance:
To deny access to a particular port (25 in this case)
/sbin/iptables -I OUTPUT -p tcp --dest 127.0.0.1 --dport 25 -j DROP
and to allow it again:
/sbin/iptables --delete OUTPUT 1
You'll need root acces for this to work, but it does have the advantage that you don't need to touch your server or client configuration.
To emulate the server down case, you could write a ServerAlwaysDown class extending your actual server, but throwing ServerException (HTTP 500) for every connection.
If you want to be thorough use always the closest you have to a production environment for the tests, put client and servers in different machines and cut the connection, then restore it.

Categories

Resources