IP fallback in android
I'm accessing a server for web service calls. When I'm developing on the same network as the server, I can access the web service by its internal IP address but not its external IP address. However, if I'm not on the network, I can only access it by its external IP address. What's the best way to try one of the IP addresses and then fall back on the other?
Here's a sample of my code for accessing only one or the other:
protected String retrieve() {
Log.v(TAG, "retrieving data from url: " + getURL());
HttpPost request = new HttpPost(getURL());
try {
StringEntity body = new StringEntity(getBody());
body.setContentType(APPLICATION_XML_CONTENT_TYPE);
request.setEntity(body);
HttpConnectionParams.setConnectionTimeout(client.getParams(), CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(client.getParams(), SOCKET_TIMEOUT);
HttpResponse response = client.execute(request);
final int statusCode = response.get开发者_StackOverflow社区StatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.e(TAG, "the URL " + getURL() + " returned the status code: " + statusCode + ".");
return null;
}
HttpEntity getResponseEntity = response.getEntity();
if (getResponseEntity != null) {
return EntityUtils.toString(getResponseEntity);
}
} catch (IOException e) {
Log.e(TAG, "error retrieving data.", e);
request.abort();
}
return null;
}
/*
* @return the URL which should be called.
*/
protected String getURL() {
return INTERNAL_SERVER_URL + WEB_APP_PATH;
}
Look at own IP address of your android. You can get it like stated here.
Then you can decide:
- if you are in subnet of your office (e.g. 192.168.0.0/16) - use internal address
- if you are in other subnet - use external address
Building on the very good comment by harism, I would simply use a static boolean to choose the IP and thus avoid pinging the wrong IP every time:
public static final Boolean IS_DEBUG = true; // or false
// all your code here
if (DEBUG)
ip = xxx.xxx.xxx.xxx;
else
ip = yyy.yyy.yyy.yyy;
This isn't exactly something you can easily fix in software. The right answer I think is fixing the filters/configuration that route traffic to your internal web server or by properly configuring DNS to return the proper IP depending on where you are (inside or outside the network). More information can be found here:
Accessing internal network resource using external IP address
http://www.astaro.org/astaro-gateway-products/network-security-firewall-nat-qos-ips-more/6704-cant-acces-internal-webserver-via-external-ip.html
and by Googling something like "external IP doesn't work on internal network"
You could put retry code in the catch clause for IOException
protected String retrieve(String url) {
Log.v(TAG, "retrieving data from url: " + url);
HttpPost request = new HttpPost(url);
try {
StringEntity body = new StringEntity(getBody());
body.setContentType(APPLICATION_XML_CONTENT_TYPE);
request.setEntity(body);
HttpConnectionParams.setConnectionTimeout(client.getParams(), CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(client.getParams(), SOCKET_TIMEOUT);
HttpResponse response = client.execute(request);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.e(TAG, "the URL " + getURL() + " returned the status code: " + statusCode + ".");
return null;
}
HttpEntity getResponseEntity = response.getEntity();
if (getResponseEntity != null) {
return EntityUtils.toString(getResponseEntity);
}
} catch (IOException e) {
if(url.equals(EXTERNAL_URL){
return retrieve(INTERNAL_URL);
}
Log.e(TAG, "error retrieving data.", e);
request.abort();
}
return null;
}
Note: Like most people have said, this probably is not a great solution for a production release, but for testing it would probably work just fine.
You could change your retrieve() call to take the host as a parameter. On startup, try to ping each possible host. Do a simple retrieve call to something that returns very fast (like maybe a test page). Work through each possible host you want to try. Once you found one that works, save that host and use it for all future calls.
1st, you should be able to handle the case on the runtime. I'd very strongly recommend vs the different builds.. For example: try the external, if fails the internal.
Some heuristics meanwhile:
Internal IP implies the network is the same. So you can check it, if it's the same try the internal address 1st, otherwise the external has precedence.
Later,save the mapping of the local ip address to the successfully connected one and look it up to alter the precedence.
The resolution itself may be carried by requesting the root '/' of the server with a timeout (you can use 2 different threads to carry the task simultaneously, if you feel like it).
Morealso, if you have access to the router/firewall it can be made to recognize the external addresses and properly handle it. So you can end up with the external address that works properly.
I would put this kind of environment-specific information in a properties file (or some kind of configuration file anyway). All you need is a file with one line (obviously you would change the IP address to what you need):
serverUrl=192.168.1.1
Java already has a built-in feature for reading and writing these kinds of files (see the link above). You could also keep database connection information etc. in this file. Anything environment-specific really.
In your code it looks like you are using constants to hold the server URL. I would not suggest that. What happens when the server URL changes? You'd need to modify and re-compile your code. With a configuration file, however, no code changes would be necessary.
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
and:
HttpClient client = new HttpClient();
HttpMethod method = new GetMethod("http://www.myinnersite.com");
int responseCode = client.executeMethod(method);
if (responseCode != 200) {
method = new GetMethod("http://www.myoutersite.com");
}
etc...
精彩评论