Set timeout for Network.connectToServer

Hi! I’m trying to build a client-server application where my android tablet connects to a server running on my PC.

In my client application I just try to connect to the server (client = Network.connectToServer(IP, PORT)), which is down. I would expect an exception to be raised immediately, but the application just stalls until a timeout is reached a few minutes later.

My question is: Is there any way of knowing inmediately after calling the connectToServer method whether the connection has been established or not?

Thank you very much.

@fegarcia said: Hi! I'm trying to build a client-server application where my android tablet connects to a server running on my PC.

In my client application I just try to connect to the server (client = Network.connectToServer(IP, PORT)), which is down. I would expect an exception to be raised immediately, but the application just stalls until a timeout is reached a few minutes later.

My question is: Is there any way of knowing inmediately after calling the connectToServer method whether the connection has been established or not?

Thank you very much.

Even in a legitimate case it can take a few seconds for a valid connection to be established. Imagine the case where host resolution takes 10 or 15 seconds because your DNS is overburdened but otherwise your connection will be fine.

It is physically impossible to “raise an exception immediately” unless you just want to drop a lot of connections for not connecting right away.

You will know when the connection happens because your listener will be notified (I assume you registered one before calling client.start()… I also assume you did call client.start() which is what is actually doing the connecting.) In the mean time, you should present status to the user and let them cancel if they feel like it has taken too long. No matter what timeout you build in arbitrarily, it will tick off some users while frustrating others anyway. There is no perfect value.

Yes, I guess you’re right… I just wish there was a way to modify the timeout, but no biggie, I’ll try your solution of presenting the status to the user and letting them cancel. Thank you very much! :slight_smile:

@fegarcia said: Yes, I guess you're right... I just wish there was a way to modify the timeout, but no biggie, I'll try your solution of presenting the status to the user and letting them cancel. Thank you very much! :)

when i establishing connection i do it on another thread
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:multithreading

in my case:

  1. on main thread, an Runnable is given to executor (scheduledthreadpoolexecutor) via executor.schedule() (im setting 0.1 sec delay because it is cool)
  2. on another thread, executor runs Runnable which contains connection logic
   ...
   Client myClient = Network.connectToServer("localhost", 6143);
   // add ClientStateListeners here
   myClient.start();
   ...

this causes that client is not frozen and you can cancel connecting action via future object (executor gives it to you)
3. when connected, i return to main thread using Callable which is for app.enqueue()
4. you are miraculously connected

myClient.start() is already doing all of the connecting on a background thread. You shouldn’t have to do all of that… in fact, Client.start() should return instantly. This is why it’s so important to register a listener to be notified when you are actually connected because there is no way to know otherwise.

@pspeed said: myClient.start() is already doing all of the connecting on a background thread. You shouldn't have to do all of that... in fact, Client.start() should return instantly. This is why it's so important to register a listener to be notified when you are actually connected because there is no way to know otherwise.

Note: if you send a message before the client is connected then that send will block until the connection is fully established. But start() should return instantly by design.

@pspeed said: myClient.start() is already doing all of the connecting on a background thread. You shouldn't have to do all of that... in fact, Client.start() should return instantly. This is why it's so important to register a listener to be notified when you are actually connected because there is no way to know otherwise.

then why window is not responding when i call start() on main thread ? (when there is no server to connect to, or connection takes long time)

@pspeed said: Note: if _you_ send a message before the client is connected then that send will block until the connection is fully established. But start() should return instantly by design.

hmm good point i will definitely try it. this can solve my small implementation glitch with adding listeners while client is connecting on another thread

Proper procedure:
(on render thread)

  1. Client myClient = Network.connectToServer(…);
  2. setup listeners
  3. myClient.start();
  4. present some kind of swirly progress bar screen

When the listener is notified of connection:
5) attach your ‘playing a network game’ app state (it is safe to attach app states from the network thread)
6) that app state stops the progress screen (or the progress screen could be removed by a second listener or whatever)

When the listener is notified of failed connection:
5) indicate error

thanks for clarification :slight_smile:

@pspeed small problem is, even if client.start() may be non-blocking, client = Network.connectToServer(host, port); is blocking :frowning: and throwing java.net.ConnectException: Connection refused: connect
so if i dont want to freeze main thread, i must run connectToserver on another thread anyway :frowning: so i must create defaultclient without using Network.connectToServer(host, port); and set up reliable and fast connectors myself (that is no problem)

Yeah, if that’s the part that’s hanging then it’s the DNS lookup:
InetAddress remoteAddress = InetAddress.getByName(host);

Note: if you already know the IP address and don’t need to do the lookup you can just do what connectToServer() is doing (it’s just a convenience method):
[java]
public static Client connectToServer( String gameName, int version,
String host, int hostPort, int remoteUdpPort ) throws IOException
{
InetAddress remoteAddress = InetAddress.getByName(host);
UdpConnector fast = remoteUdpPort == -1 ? null : new UdpConnector( remoteAddress, remoteUdpPort );
SocketConnector reliable = new SocketConnector( remoteAddress, hostPort );

    return new DefaultClient( gameName, version, reliable, fast, new TcpConnectorFactory(remoteAddress) );
}

[/java]

hmm it still has some freeze by using InetAddress.getByName(“127.0.0.1”)… i think i search for how to create instance of InetAddress by myself

@Ascaria said: hmm it still has some freeze by using InetAddress.getByName("127.0.0.1")... i think i search for how to create instance of InetAddress by myself

Yes, you’d have to replace that host name lookup with just creating the inet address. Sorry, I thought that was obvious:
https://docs.oracle.com/javase/6/docs/api/java/net/InetAddress.html#getByAddress(byte[])

got it, but encountered another problem. with one Client i can connect to only one source only once. client have all needed listeners. but when i disconnect gameclient from gameserver and wants to connect to another gameserver im stucked, because after client.close(), i cannot change address and also im unable to do client.start again, because channels exists and throws java.lang.IllegalThreadStateException… thread state is 2 not 0

i cannot simply replace client because listeners were added

so my only chance is my own Client, i wanted to avoid to do that :frowning:

@Ascaria said: got it, but encountered another problem. with one Client i can connect to only one source only once. client have all needed listeners. but when i disconnect gameclient from gameserver and wants to connect to another gameserver im stucked, because after client.close(), i cannot change address and also im unable to do client.start again, because channels exists and throws java.lang.IllegalThreadStateException... thread state is 2 not 0

so my only chance is my own Client, i wanted to avoid to do that :frowning:

I don’t understand what you mean actually. Are you trying to reuse the same Client instance? Why on earth would you want to? If that’s not it then you will have to explain better because I’ve said “Maybe he means…” three different times already reading this.

@pspeed said: I don't understand what you mean actually. Are you trying to reuse the same Client instance? Why on earth would you want to? If that's not it then you will have to explain better because I've said "Maybe he means..." three different times already reading this.

i want to preserve client instance, because when every app state initializes it adds listeners to client, so only once and app states are not removed when connection is terminated. i cannot remove world app state, because when client is disconnected, “hangar world” is loaded with user’s fleet, like in world of tanks…
my game client should be able to connect to different game servers, because i want to allow users to create their own game servers and worlds, like minecraft and others not-mmo do
i also have “central server” where user accounts and ships are stored, so when you connect to another game server, your space ship is still there

@Ascaria said: i want to preserve client instance, because when every app state initializes it adds listeners to client, so only once and app states are not removed when connection is terminated. i cannot remove world app state, because when client is disconnected, "hangar world" is loaded with user's fleet, like in world of tanks... my game client should be able to connect to different game servers, because i want to allow users to create their own game servers and worlds, like minecraft and others not-mmo do i also have "central server" where user accounts and ships are stored, so when you connect to another game server, your space ship is still there

Something is messed up with your design, I think. For example, in Minecraft you’d create completely new game app states that were using the new client. You wouldn’t try to cram the new underlying state into the existing world state… it’s way painful.

So yes, crazy designs will require crazy solutions. Or you could separate your world-specific app states from the others (the base services) so that you attach those when connected. Connection closing means tearing the old ones down. Otherwise, the issue is the least of the pain you have in front of you.

yea so i need to be able to add all listeners on demand to new client… it will be some refactoring, but why not :slight_smile: its like from bad to worse :slight_smile:

i do not frequently add or remove app states on runtime, my world state can fill rootnode and also clean it out, so there is no reason to detach world state or manipulate with its listeners (some world is always loaded. “gameplay” world or “hangar” world)
maybe something is wrong with my design, but i dont see reason to “reload whole car” (client, listeners etc) while i want only “another color” (ip address)

@Ascaria said: yea so i need to be able to add all listeners on demand to new client.. it will be some refactoring, but why not :) its like from bad to worse :) ... i do not frequently add or remove app states on runtime, my world state can fill rootnode and also clean it out, so there is no reason to detach world state or manipulate with its listeners (some world is always loaded. "gameplay" world or "hangar" world) maybe something is wrong with my design, but i dont see reason to "reload whole car" (client, listeners etc) while i want only "another color" (ip address)

Except that IP address points to a whole new world containing cars, trees, buildings, everything… every bit of game state you had before is now wrong.

Generally, the NetworkGameState or whatever should be created when the connection comes through and it’s what adds the message listeners on initialize() and removes them again on cleanup(). If you get in the habit of writing all of your app states this way your whole life will be easier. Trust me, your whole app will be cleaner if you do all of this properly.

You can look at a somewhat convoluted example of what I mean if you look at Monkey Trap. It handles both single player and multiplayer. The only states setup during app startup are global + menu state. Single player and multiplayer states are attached as needed… though I’m not sure I ever got around to letting the user back out and start a new game.