I've written an Android code that tries to access a server over the network upon an incoming call.
Network access is executed once an incoming call is received using a BroadcastReceiver with the intent-filer of READ_PHONE_STATE.
Data communication is performed over GPRS.
Using the logcat I've noticed that the data retrieval time from the network has large distribution, ranging from 1sec to 15sec.
Here is the code for sending a HTTP request and waiting for server response
urlConnection = (HttpURLConnection) url.openConnection();
Log.i("TIMINGS", "Connected to server.");
jsonobj.put("id", id);
String requestContent = jsonobj.toString();
urlConnection.setDoOutput(true); // Will make a POST HTTP request
urlConnection.setFixedLengthStreamingMode(requestContent.length());
urlConnection.setRequestProperty("content-type", "application/json");
urlConnection.setRequestProperty("Connection", "close");
System.setProperty("http.keepAlive", "false");
urlConnection.setDefaultUseCaches(false);
Log.i("TIMINGS", "Sending request...");
// Write the JSON string to the POST request content
DataOutputStream out = new DataOutputStream(urlConnection.getOutputStream());
out.writeBytes(requestContent);
out.flush();
out.close();
Log.i("TIMINGS", "Request sent.");
Log.i("DEBUG", "Connect response code: " + urlConnection.getResponseCode());
// Get the output from the server
Log.i("TIMINGS", "Extracting data from response...");
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
byte[] bytes = getBytesFromInputStream(in);
if (bytes == null) {
return null;
}
in.close();
Log.i("TIMINGS", "Response extracted");
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (urlConnection != null) {
Log.i("TIMINGS", "Disconnected.");
urlConnection.disconnect();
}
}
When network access is performed via WIFI, results are consistently fast.
Has anyone encountered similar results? Can something be done to achieve consistent fast network access via GPRS when an incoming call is received?
Thanks,
Latency is highly variable on a mobile network primarily due to the science, maths and engineering of capturing and recovering errors on a very poor transmission path (the atmosphere) AND managing the mobililty aspect.
Your device must request available resources from the network and wait until they are granted. On GSM networks voice traffic has priority, so this can take some time (yes 15 seconds is ok) - devices are told to queue the data.
On 3G networks the device is told every 4 milliseconds about the quality of the signal received at the tower. Your device must change the coding rate in accordance with the quality reported. The amount of redundant coding in the transmission is set so that your data can be recovered with a high degree of reliability. The constant variability in the coding rate impacts throughput and latency.
GSM-GPRS degrades like a brick wall, 3G-GPRS (modern) is a shared channel so degrades gracefully
Due to the poor transmission path for WiFi (impacted by water in atmosphere) - it can only exist across the room, so the scope for the variability can be much tighter without sudden degradation.
Related
We are developing some internal apps for mobile devices that are connected to internal wifis. We have some problems because we are only checking if the devices are connected using ConnectivityManager. But we need to check not only if there is connection, we need to check that the connection between the device and the server is working.
The problem is that ConnectivityManager tell us that the wifi is connected. But if the device is in an area with little coverage the app have errors trying to connect.
How can we easily check that the connection we have open against the server is still responding correctly? For example, one of the applications the connection is open against a SQL Server. Is there any way to check that we get to the server and it gives us an ok, and that we are not losing the connection and the packages because of the low coverage?
Thanks!!
You can try pinging the server if you receive a NullPointerException or IOException most likely there is no connection or connection timed out.
you can read more here an answer to similar question by syb0rg. Also remember to wrap this piece of code in an AsyncTask or a Thread to prevent your app from crashing.
try {
//replace URL with your domain
URL url = new URL("http://www.google.com");
HttpURLConnection urlConnect = (HttpURLConnection) url.openConnection();
urlConnect.setConnectTimeout(1000);
urlConnect.getContent();
System.out.println("Connection established.");
} catch (NullPointerException np) {
np.printStackTrace();
} catch (IOException io) {
io.printStackTrace();
}
In my app I have a Socket like this:
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), 5000);
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
I write to this socket like this:
outputStream.write((message).getBytes());
And read from it like this:
while (true) {
try {
String message = inputStream.readLine();
if (message != null) {
//do sth here
}
} catch (IOException e) {
e.printStackTrace();
}
}
Now the problem is sometimes Internet connection is so slow, in a way that connection stays alive (socket stays connected) but cannot send any messages. In such conditions it seems like the OutputStream holds the messages until it is able to deliver them to server. All I want is know when the socket is in this state?
I can ask the above question in this way too: How can I know if the written message is delivered to server?
Note that checking whether device is connected to Internet or not doesn't help because the device is really connected but probably experiencing a poor connection.
All I want is know when the socket is in this state?
It gets in that state when TCP acks from the server are not received by the client so those packets can be removed from the client's outgoing buffer. As long as there is room in the buffer, a send will not block the caller. But once the buffer fills up, subsequent sends block the caller until the buffer clears up.
The only way I know to detect this condition is to use the socket's getChannel() method to access its underlying SocketChannel, and then register() that with a Selector that you can then monitor/query using its select() method to know if the socket is writable (has room in its outgoing buffer).
But, that will only be able to tell you if the socket can accept at least 1 byte for sending without blocking the caller. It will NOT tell you if a given message can be accepted completely for sending, as the buffer could fill up before the end of the message is reached, in which case the send will block until the buffer frees up room for the whole message.
But, it would let you monitor the health of the socket connection, and if you find that sends are getting blocked too much due to slow/missing acks, you might have to just close the socket and reconnect.
How can I know if the written message is delivered to server?
The only way is to have the server send back its own message after receiving the client's message.
I have implemented a method to check for internet connection
public boolean isInternetWorking() {
boolean success = false;
try {
URL url = new URL("https://google.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000);
connection.connect();
success = connection.getResponseCode() == 200;
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
The code works fine. However, I came across many cases where my app would not work properly due to slow internet connection.
Is there any way to show an alert message when the internet connection is slow and the data takes a long time to load?
Facebook has released a library for checking connection, Network Connection Class
Here the excerpt:
Network Connection Class is an Android library that allows you to figure out the quality of the current user's internet connection. The connection gets classified into several "Connection Classes" that make it easy to develop against. The library does this by listening to the existing internet traffic done by your app and notifying you when the user's connection quality changes. Developers can then use this Connection Class information and adjust the application's behaviour (request lower quality images or video, throttle type-ahead, etc).
Or maybe you can use Speed Test library
Read the near similar QA:
Best way to evaluate connection speed
Calculating Internet Speed in android
How to detect Internet connection speed with Java?
For WiFi link speed check WifiInfo.getLinkSpeed()
For Mobile Data Link you can only check
TelefonyManager.getNetworkType() to determine the current Mobile Data
Link type. You should then aproximate to actual speed by link type
(i.e. for GPRS up to 128 kbps, for EDGE up to 236.8 kpbs, for 3G up
to 2 Mbps, for HDSPA up to 7.2 Mbps). Take into consideration that
this is only an aproximation. Your could be conneting using HDSPA but
your carrier limiting the top speed to 2 Mbps.
Now , you have to get speed and put condition whether below 100kbps , "low internet connection"
I am currently trying to send some data between two android devices using Bluetooth. I've read plenty of questions regarding bluetooth transfer, sockets, and streams. So far without any luck.
The connection part is working. I get the device address then open a connection using the following :
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(myOtherDeviceAdress);
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(myUUID));
socket.connect();
And then try to send some data using the OutputStream
OutputStream mmout=tmp.getOutputStream();
byte[] toSend="Hello World!".getBytes();
mmout.write(toSend);
mmout.flush();
On the receiving end:
mBluetoothServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("ccv_prototype", UUID.fromString(myUUID));
mBluetoothSocket = mBluetoothServerSocket.accept(3 * 1000);
InputStream is = mBluetoothSocket.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(is));
And then, different version trying to read the buffer, currently:
int c;
StringBuilder response = new StringBuilder();
try {
while ((c = r.read()) != -1) {
//Since c is an integer, cast it to a char. If it isn't -1, it will be in the correct range of char.
response.append((char) c);
}
} catch (IOException e) {
e.printStackTrace();
}
String result = response.toString();
Log.d("MyTag", "Received String: " + result);
My issue here is that if I don't close the OutputStream, the receiving end never receives the EOF, but if I add mmout.close();, it closes before it even had time to read the message I wanted to send. So far, my only idea is to send a specific token as an EOF but this doesn't sound right.
What did I miss ?
Any help appreciated.
The simple answer is yes. You should send a specific token to represent EOF. When you do a read() operation on a Bluetooth socket, it will either return immediately with some data if there's data ready to be read, or otherwise the read() call will block until there is some data, or some IO exception happens (e.g. the connection drops). This is why you must make use of Threads, particularly for Bluetooth socket read and write operations. What you're attempting to do is rely on the BufferedReader returning -1 to indicate "no more data". Sadly, this isn't how it works. The -1 will only happen in the event of some IO exception or the connection closing.
Detection of where your piece of information (i.e. your packet of data) starts and finishes, or indeed determining when an overall communication session is ended, is something that you handle yourself in your own application protocol (or of course an existing protocol) that works over the sockets. This is an important concept with any protocol that works through streaming sockets. A good example to look at is HTTP, which as you know is conventionally used over TCP. Taking a quick look at HTTP will show you (a) how the HTTP protocol uses headers to tell the recipient how many more bytes to expect for the overall HTTP "message", and (b) how HTTP headers are also used to negotiate when the connection should close. What you cannot do is attempt to use methods on the sockets themselves to determine when the sender has finished writing a message. Similarly if one end is to be aware that the other end wants to close the connection, that should be negotiated over the application protocol.
I'm having an intermittent connectivity issue with Android 4.1.2 and 4.2.2 whereby the HTTP stack seems to completely time-out (DNS Lookup and TCP/IP still works I can check that using ADB SHELL).
These connections are failing over GPRS, not over WiFi.
When checking netstat using SHELL I can see that the connections are sat waiting at SYN_SENT, and when inspecting the firewall on the server, we can see that it has responded to the SYN request but hasn't heard anything back from the device. During this outage it seems that all HTTP traffic fails on the device. Exchange no longer works and you cant request any pages using any of the common browsers (Firefox, Chrome), even though the device reports network and you can make / receive calls.
My application communicates over HTTP and HTTPS and during this outage both fail. POST and GET to my JSON web service, the requests hang and throw:
java.net.SocketTimeoutException: failed to connect to mywebaddress.com/1.1.1.1 (port 443) after 10000ms
This is expected if it's having trouble connecting as it is respecting the timeouts I have set below with the HttpURLConnection.
The code I use is as follows, located in an Async class. This is often contained in a connection loop, depending on how important the message is. This may be called up to 3 times with a 15 second gap in-between each call.
HttpURLConnection conn = null;
BufferedReader reader = null;
try
{
// Uses ConnectivityManager.getActiveNetworkInfo()
// Returns true, the Android OS reports a connection
if(myApp.hasNetworkConnection)
{
conn = (HttpURLConnection)endpoint.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(20000);
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer strResults = new StringBuffer();
String strLine = "";
while ((strLine = reader.readLine()) != null)
{
strResults.append(strLine);
}
log(strResults.toString());
}
}
catch(Exception ex)
{
log(ex.getMessage());
}
finally
{
if(reader != null)
try
{
reader.Close();
reader = null;
}
catch(Exception ex)
{
ex.printStackTrace();
}
if(connection != null)
{
connection.disconnect();
connection = null;
}
}
I'm wondering if anyone has experienced this in the past, is the method of regularly trying the connection the wrong approach and exhausting the connection pool?
The connections that are created fire regularly and it doesn't (currently) batch connections together.
Just to share a bit more info, this is what netstat shows on the device (ADB Shell) when we experience the outage. The two "ESTABLISHED" connections are TCP connections not HTTP requests. The mobile has signal and the data symbol is showing a connection.
try this
try {
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("HEAD");
con.setConnectTimeout(5000); //set timeout to 5 seconds
return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
} catch (java.net.SocketTimeoutException e) {
return false;
} catch (java.io.IOException e) {
return false;
}
Just thought I would update, in-case this issue is experienced by anyone else.
The problem here is not with Android but the web service that the application is calling. Looking at it, when it was calling the JSON web service, it was taking far longer than expected (up to and over a minute) to return a response (if at all) and that response was always 200/OK.
As we were saturating the Android HTTP Stack with requests, eventually they were not clearing in a good enough time and would freeze up the device.
I added better fault tolerance to my code by setting the connect and read timeouts to more appropriate values rather than "0" (infinite) and we have looked at modifying the web service so that long running processes return a 201/Accepted status so that the device can carry on doing it's own work. We call the device back by other means (Google Cloud Message) once server processing has been completed.