I am currently using the HTTPClient 4 to make a POST request to a remote server like this:
HttpResponse response = httpClient.execute( request );
InputStream is = response.getEntity().getContent();
When the server is not reachable it takes a self-configured amount of time before the connection actually times out. During that periode the execute() is a blocking call.
What i am looking for is a way to cancel the execute() before the natural timeout so that my thread running the execute() is not blocked anymore and will finish gracefully.
I have tried
request.abort();
and
httpClient.getConnectionManager().shutdown();
But both of these calls do not interrupt the execute(). Is there any other way to cancel the ongoing connection attempt?
Wrap the call in a Future and invoke get with timeout.
I think you can set connectionTimeout for whatever the max time you want using these methods:setConnectionTimeout()
setSoTimeout()
Related
I have found a class that handles TCP connections and I am using it to communicate with a gaming service. All was working fine until I realized that my application was stalling if the connection speed was slower. I have a thread polling let's say every 30 seconds.
I got the TCPClient class I use from this thread Java TCP sending first message, then infinite wait
This service requires 2 steps to verify a request. You first send a hash and you receive and acknowledge. Then you send the the actual request and you receive the response.
public byte[] getResponse(byte[] hash, byte[] request) throws Exception{
if(client == null || client.socket.isClosed() || !client.socket.isConnected()
|| client.socket.isInputShutdown() || client.socket.isOutputShutdown(){
client = new TCPClient(this.host, this.port);
}
client.SendToServer(hash);
byte[] ack = client.ReceiveFromServer();
if(checkAck(ack, getAckForRequest(request))){
client.SendToServer(request);
byte[] response = client.ReceiveFromServer();
return response;
}
}
My code looks something like this. I simplified it a bit to make it more readable.
I am using this function inside a try/catch block and when it throws an exception I store the request in a MySQL database.
Is there a way to avoid blocking my main thread if the connection is slow and do the same stuff?
Is there a way to avoid blocking my main thread if the connection is slow and do the same stuff?
Yes. One can call setSoTimeout() on a Socket.
The Oracle documentation:
https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#setSoTimeout-int-
Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid. The option must be enabled prior to entering the blocking operation to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout.
If you just want to close the connection and give up it works well. If you want to resume the action later you have to keep track of the bytes already read which means just having more threads is usually an easier option.
i have a thread that is connecting to a url to obtaining some data.
Sometimes the method httpConnection.connect(); taked too munch time to get the response, and i want to limit the loading dialog of this connection thread to 5 seg.
I tryed adding timeouts into the code, but it doesn't work!!
URL formattedUrl = new URL(url);
URLConnection connection = formattedUrl.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
HttpURLConnection httpConnection = (HttpURLConnection) connection;
httpConnection.setAllowUserInteraction(false);
httpConnection.setInstanceFollowRedirects(true);
httpConnection.setRequestMethod("GET");
httpConnection.setConnectTimeout(5000);
httpConnection.setReadTimeout(5000);
httpConnection.connect();
So, i must stop the connect method and the thread when 5000 seconds have passed or when the used has pressed the back key on the phone.
How can this be achieved? i can't find info about doing this work in android with a url connection thread.
thanks
The timeout settings on URLConnection are not sufficient to provide the timeout control desired. The reason being is:
setConnectTimeout() sets the timeout for just establishing a connection with the server. So, a timeout will only be triggered if when opening the connection, the connection can not be established in the prescribed amount of time.
setReadTimeount() sets the timeout for reading available data. For this, a timeout will only be triggered if any, single read operation blocks for longer than the set amount of time. So, it is quite possible even on a slow connection that each read operation never approaches the timeout threshold but, the total amount of time to read all the data is quite lengthly.
One solution to apply a timeout to the entire read unit of work, is to use the concurrency capabilities found in Java 5 and beyond. In particular, the use of an ExecutorService and Future should be sufficient.
Runnable task = new Runnable() {
public void run() {
// original code to read data from a URL
}
};
ExecutorService executor = Executors.newSingleThreadExecutor(); // or any other implementation
Future<?> future = executor.submit(task);
try {
future.get(5, TimeUnit.SECONDS); // wait 5 seconds for task to complete
// success
} catch (TimeoutException ex) {
// handle timeout
} finally {
executor.shutdownNow(); // cleanup
}
Brent Worden's answer is on the right track. But there is a problem with his solution. If the task timeout kicks in, the thread that called future.get will get an exception as expected. However, the worker thread that was executing the Runnable.run() method may still be stuck waiting for the connect or read to complete.
Solving this is difficult. As far as I'm aware, the only reliable way to unjam a thread that is waiting on a socket connect or socket stream read or write is to call close() on the Socket object. And the problem with using that approach (here) is that the standard HttpUrlConnection object doesn't expose the Socket object.
My recommendation would be to use the Apache Http client libraries. This question explains how to abort a request if you use HttpClient: Cancel an HttpClient request
You only need to invoke the URLConnection.setConnectTimeout(millis) to achieve what you asking. If the specified timeout expires a SocketTimeoutException is thrown.
try {
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setConnectTimeout(5000); //set timeout to 5 seconds
} catch (java.net.SocketTimeoutException e) {
//DO SOMETHING
} catch (java.io.IOException e) {
//DO SOMETHING
}
It's worth noting that it says the following:
Some non-standard implmentation of this method may ignore the
specified timeout. To see the connect timeout set, please call
getConnectTimeout().
You can't, the process which the thread belongs to will end if you stop the thread.
I am using org.apache.http and I've this code:
DefaultHttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(url);
HttpResponse resp = client.execute(get);
HttpEntity entity = resp.getEntity();
InputStream input = entity.getContent();
...
//Read the bytes from input stream
This is the code I am using to download files over Http, I want to cancel the connection(may be user chooses to) What is the graceful way to close the connection. I found 2 ways, Both cancels the download.
Closing inputsteram, input.close(); which causes IOException.
Aborting HttpGet object, get.abort() causes SocketException.
I have try catch, so no erros, but without throwing exception,
is there a way to cancel or abort the connection?
What is the right way to go about it ?
The proper way doing this is sending FIN value to the server side.
How ever in android you do not have the option to be involved in this level, so you can implement by your self using C, or use one of the methods you mention in your question.
Using HttpUriRequest#about is the right way in my opinion. This will cause immediate termination of the underlying connection and its eviction from the connection pool. Newer versions of HttpClient (4.2 and newer) intercept SocketExceptions caused by premature request termination by the user. The problem is that Google ships a fork of HttpClient based on an extremely outdated version (pre-beta1). If you are not able or willing to use a newer version of HttpClient your only option is to catch and discard SocketException in your code.
Use this
client.getConnectionManager().closeExpiredConnections();
client.getConnectionManager().shutdown();
Now you can decide where would you like to write these 2 lines in code.. It will close the connection using the DefaultHttpClient object that you created.
Let me know if this helps you.
Try to cancel the task when you want to interrupt the connection:
task.cancel(true);
This will cancel the task and the threads running in it.
Check this for reference:
public final boolean cancel (boolean mayInterruptIfRunning)
Since: API Level 3
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
Parameters
mayInterruptIfRunning true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
Returns
false if the task could not be cancelled, typically because it has already completed normally; true otherwise
I'm running into an issue when i try to use the HttpClient connecting
to a url. The http connection is taking a longer time to timeout, even after i set
a connection timeoout.
int timeoutConnection = 5000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
int timeoutSocket = 5000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
It works perfect most of the time. However, every once in while, the http connection runs for ever and ignore the setconnectiontimeout, especailly when the phone is connected to wifi, and the phone was idling.
So after the phone is idling, the first time i try to connect, the http connection ignores the setconnectiontimeout and runs forever, after i cancel it and try again, it works like charm everytime. But that one time that doesn't work it creates a threadtimeout error, i tried using a different thread, it works, but i know that the thread is running for long time.
I understand that the wifi goes to sleep on idle, but i dont understand why its ignoring the setconnectiontimeout.
Anyone can help, id really appreciated.
Not sure if this helps you, however I think it's worth sharing here. While playing with the timeout stuff I found there is a third timeout type you can assign:
// the timeout until a connection is established
private static final int CONNECTION_TIMEOUT = 5000; /* 5 seconds */
// the timeout for waiting for data
private static final int SOCKET_TIMEOUT = 5000; /* 5 seconds */
// ----------- this is the one I am talking about:
// the timeout until a ManagedClientConnection is got
// from ClientConnectionRequest
private static final long MCC_TIMEOUT = 5000; /* 5 seconds */
...
HttpGet httpGet = new HttpGet(url);
setTimeouts(httpGet.getParams());
...
private static void setTimeouts(HttpParams params) {
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
CONNECTION_TIMEOUT);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SOCKET_TIMEOUT);
params.setLongParameter(ConnManagerPNames.TIMEOUT, MCC_TIMEOUT);
}
I've met the same problem, I guess maybe the Android doesn't support this parameter.
In my case i tested all three parameters for the ThreadSafeClientConnManager
params.setParameter( ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(20) );
params.setIntParameter( ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 200 );
params.setLongParameter( ConnManagerPNames.TIMEOUT, 10 );
ThreadSafeClientConnManager connmgr = new ThreadSafeClientConnManager( params );
The first and second worked fine, but the third didn't work as documented. No exception was thrown and the executing thread was blocked indefinitely when the DefaultHttpClient#execute() was executing.
see http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e650
"...One can ensure the connection manager does not block indefinitely in the connection request operation by setting 'http.conn-manager.timeout' to a positive value. If the connection request cannot be serviced within the given time period ConnectionPoolTimeoutException will be thrown."
Thread t=new Thread()
{
public void run()
{
try
{
Thread.sleep(absolutetimeout);
httpclient.getConnectionManager().closeExpiredConnections();
httpclient.getConnectionManager().closeIdleConnections(absolutetimeout,TimeUnit.MILLISECONDS);
httpclient.getConnectionManager().shutdown();
log.debug("We shutdown the connection manager!");
}
catch(InterruptedException e)
{}
}
};
t.start();
HttpResponse res= httpclient.execute(httpget);
t.interrupt();
Is that along the lines of what you all are suggesting?
I'm not exactly sure how to cancel the execute once it has started, but this seemed to work for me. I'm not sure which of the three lines in the thread did the magic, or if it was some combination of all of them.
You could manage the timeouts yourself, this way you can be confident that no matter what state the connection gets in, unless you receive an acceptable response, that your timeout will fire and the http request will be aborted.
I've had similar issues with timeouts on android. To resolve it what I did was used the commands to not let the phone idle while I was attempting to establish a connection and during any reads or writes to the connection. Its probably worth a shot in this case as well.
Although I haven't seen this on the Android platform, I've seen similar things on other platforms and the solution in these cases is to manage the timeout yourself. Kick off another thread (the timeout thread) when your make your request. The timeout thread counts down the requisite time. If the timeout expires before you receive any data, the timeout thread cancels the original request and you retry with a new request. Harder to code, but at least you know it will work.
From you snippet it's not ultimately clear if you set the timeouts before calling HttpClient.executeMethod(..). So this is my guess.
Well, if you idle/multitask to another application, then your thread that is running might be stopped and destroyed. Maybe you should put the connection code inside a Service instead?:
http://developer.android.com/reference/android/os/AsyncTask.html
http://developer.android.com/reference/android/app/IntentService.html
How are you making the HTTP Connection? This looks like a threading issue. If you are using a background thread, then the thread may be killed along with any timeout registered. The fact that it works the next time tells me that your code will work, if you make the call in a android component and manage the WAKE_LOCK on it yourself. Anyways please post more information about the calling mechanism?
The problem might be in the Apache HTTP Client. See HTTPCLIENT-1098.
Fixed in 4.1.2.
The timeout exception tries to reverse DNS the IP, for logging purposes. This takes an additional time until the exception is actually fired.
I am new in Apache HttpClient, I used the following code to get the HTTP connection timeout (disconnected) after certain time interval:
PostMethod method = new PostMethod(authURL);
HttpClient client = new HttpClient();
HttpClientParams params= new HttpClientParams();
params.setParameter(params.CONNECTION_MANAGER_TIMEOUT, 10); //10 Nano second
client.executeMethod(method);
but it wait for more than one minute without any hope to timeout/disconnect? Where can the problem be?
There are 2 timeouts involved in HTTPClient, try to set both,
client.getHttpConnectionManager().
getParams().setConnectionTimeout(5000);
client.getHttpConnectionManager().
getParams().setSoTimeout(5000);
However, the values will be ignored if the connection is stuck in a native socket call. So you might have to run the request in a different thread so you can time it out. See my answer to this question on how to do that,
java native Process timeout
The connection manager timeout triggers when the act of trying to get a connection from your connection manager takes too long. This is not the same as the timeout for the http connection itself. Use HttpClientParams.setSoTimeout() instead.
http://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/params/HttpMethodParams.html#setSoTimeout%28int%29
Have you looked at setting SO_TIMEOUT ?
Sets the socket timeout (SO_TIMEOUT)
in milliseconds to be used when
executing the method. A timeout value
of zero is interpreted as an infinite
timeout.