Android: Best practice to read data from socket - android

I am trying to get around Sockets in Android. Especially I want to know what is best practice to read data from socket and present it to UI.
As per I understand, we cannot have call to read data in the main UI thread as it is a blocking operation.
So I got these code snippets reading data from socket. (Btw, I've picked up these snippets from voted up SO questions):
This...
SocketAddress sockaddr = new InetSocketAddress("192.168.1.1", 80);
nsocket = new Socket();
nsocket.connect(sockaddr, 5000); //10 second connection timeout
if (nsocket.isConnected()) {
nis = nsocket.getInputStream();
nos = nsocket.getOutputStream();
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
byte[] buffer = new byte[4096];
int read = nis.read(buffer, 0, 4096); //This is blocking
while(read != -1){
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
publishProgress(tempdata);
Log.i("AsyncTask", "doInBackground: Got some data");
read = nis.read(buffer, 0, 4096); //This is blocking
}
And this...
clientSocket = new Socket(serverAddr, port);
socketReadStream = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String line = null;
String stringToSend = "This is from client ...Are you there???";
//Write to server..stringToSend is the request string..
this.writeToServer(stringToSend);
//Now read the response..
while((line = socketReadStream.readLine()) != null){
Log.d("Message", line);
Being newbe to android development I like to know:
What is difference between these two ways of reading?
First one was written as AsyncTask while second one was intended to run as separate thread. Which one is correct approach?
Is there any better way to read from socket? (e.g. using non-blocking sockets, callbacks, using any popular third party library etc.)

What is difference between these two ways of reading?
The second approach used BufferedReader, which has a internal buffer mechanism, which make you write less code.
First one was written as AsyncTask while second one was intended to run as separate thread. Which one is correct approach?
AsyncTask is a wrapper of Thread, using AsyncTask can do network operation in the background thread and publish result int the ui thread.AsyncTask also manages the Thread pool, in some cases, you need not create a new thread every time. It is recommended to use AsyncTask in Android.
Is there any better way to read from socket? (e.g. using non-blocking sockets, callbacks, using any popular third party library etc.)
You can use Square's okio, it is a better IO library for java, and has Buffer support.

Related

Cannot VIew the Data Sent By a TCP Packet Sending Program(Packet Server)

I'm trying to develop a small app which receives some data via sockets and based on the data it receives,it prints a toast message for the user.I' getting the data,but the data apparently cannot be read properly.Here is the relavent portion for the same.
int red = -1;
byte[] buffer = new byte[1024]; // a read buffer of 5KiB
byte[] redData;
while ((red = cs.getInputStream().read(buffer)) > -1) {
String redDataTextappend;
redData = new byte[red];
redDataTextappend = new String(redData);
Log.w("Data got",redDataTextappend);
if (redDataTextappend == "hi")
{
//Display Toast message using runonUIThread(new Runnable);
}
else
{//Display Message Using runonUITHread(new Runnable);
}
This code runs on a separate thread as android does not allow networking operations on a separate thread.
The 4 Diamonds is the data displayed by the android studio and cs is the name of the socket which accepts connections.
Thanks.
You are simply printing the String that encodes to zero bytes since you never copy the data that is read in.
It would make more sense to convert the byte array to a hex string if the array contains arbitrary data: see the answers to this question for options for that.
If the array contains the encoding of a String in some charset, for example UTF-8, then do the following:
byte[] redData = Arrays.copyOf(buffer, red);
String redDataTextappend = new String(redData, Charset.forName("UTF-8"));
Log.w("Data got",redDataTextappend);

Android read/write in same socket from ServerSocket

I have an android app which will create a ServerSocket and accept a socket.
I want it can communicate(read/write) with remote device.
My sample code like following:
mListenSocket = new ServerSocket();
mListenSocket.setReuseAddress(true);
mListenSocket.bind(new InetSocketAddress(DOCK_PORT));
mSocket = mListenSocket.accept();
Thread {
loop {
outputStream = mSyncSocket.getOutputStream();
inputStream = mSyncSocket.getInputStream();
...
inputStream.read(data)
outputStream.write(data);
...
}
}
It can read right inputStream data from client, but the second time read() always return -1 after I write data in outputStream firt time .
I have no idea about this issue.
Somebody can give me some tips? Thanks a lot.
======================================================================
I think I need to express my problem more clearly.
There are two devices(A, B), and their workflow as follows:
Type 1 task:
1. A sends command to B
2. B receives command and reply message to A
3. A receives message, Done.
Type 2 task:
1. A sends command to B, Done.
Above tasks are asynchronized.
My old method was that create a ServerSocket on A and B device respectively to handle A->B and B->A communication.
I think maybe one socket can resolve above task, but one socket will encounter read -1 issue.
Someone can give me more advices? Thanks a lot.
read() returns -1 because the peer has closed the connection. You should do likewise, and stop reading, and stop handling the connection completely. It's finished.
I use something like this:
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
// Send the login data.
final JSONObject login = new JSONObject();
// Send the login message.
dos.write((login.toString() + "\r\n").getBytes());
// Wait until we receive something from the server.
String receivedMessage;
while ((receivedMessage = in.readLine()) != null) {
Log.i(TAG, "Received data: " + receivedMessage);
processMessagesFromServer(dos, receivedMessage);
}
I use a normal socket instead of ServerSocket.
Please check this thread for more information.

How to read available text (not ending with new line) from TCP socket using BufferedReader

I'm communication with a server through a tcp socket connection, i'm able to read lines that ends with \n fine, however when the line is not terminated (ends in \n) i'm not able to read it. I tried the following but it didn't work and caused my app to freeze at startup:
private Socket socket;
private BufferedReader input;
public boolean isConnected;
#Override
public void onCreate()
{
try
{
socket = new Socket ("server.ip.add.ress", 23456);
input = new BufferedReader (new InputStreamReader (socket.getInputStream());
handshake();
isConnected = true;
}
catch // Handle IOException and UnknownHostException
}
// custom runnable to read availabe input from the server
private class MyRunnable implements Runnable
{
private volativle String value;
public String getValue()
{
return value;
}
#Override
public void run()
{
int count;
char[] buffer = new char[10]; // expected message 'username: '
try
{
count = input.read (buffer, 0, 10);
if (count > 0) value = new String (buffer);
}
catch // IOException
}
}
// when connection is established with server expect 'username: ' from
// the server and send the user name back to it
public void handshake()
{
MyRunnable runnable = new MyRunnable();
try
{
Thread thread = new Thread (runnable);
thread.start();
thread.join();
String greeting = runnable.getValue();
if (greeting.equals ("username: ")) // Send username back
}
catch // InterruptedException
}
why is it hanging? and how can i read a non terminated line?
Edit:
To clarify: The server sends the greeting message username: immediately after the connection is established with a client, the client wait for the greeting and send back it's username when received (that's what handshake() does), if no handshake the client disconnects otherwise it start listening for incoming messages. Because i need to know if handshake is complete before starting the listener i had to use Thread.join().
The problem: Thanks for the comments and answers below, it turned out that BufferedReader.read() blocks the thread and waits until something is sent from the server and if nothing is being sent it causes the app to hang, Therefor there's no way to find out if the line has ended.
The solution: In my specific situation i just wanted to know if a specific message is sent "username: " so i used read (buffer, 0, 10) to read exactly 10 characters (the length of "username: "), and because it blocks if nothing is sent i used Thread.join (1000) which waits only one second and then if nothing received i disconnect the client.
Why is it hanging?
This is what it is suppose to be. It will block the thread if no data is available to read. This is also why you want to put it in a background thread.
Can it not just return if nothing is available?
What you are looking for is ready(), which will tell you whether there is available data or not.
Indicates whether this reader is ready to be read without blocking.
Returns
true if this reader will not block when read is called, false if unknown or blocking will occur.
But you should be very careful when using this function. Because networking is a lot about timing. The fact that you don't have any data to read at this second doesn't necessary mean that it won't be any data in the next second.
So a better design of the server should be more or less as the following:
If the username is found, return the username
If the username is not found, return an error message to let the client side know that the username is not found
There's no need for the thread. Your goal is to wait until you've read what you've been waiting for. Why not just let read() perform the wait for you?
What you're struggling with is the classic problem of TCP communication: "when do I know that I've got everything the server sent?"
In your case, you're expecting to read bytes until the collection of bytes ends with "username: ". So, change your algorithm to perform 1 byte reads (filling a buffer as you go) until that buffer ends with "username: ".
You can make a more complicated algorithm -- which would be more efficient -- that would attempt to read multiple bytes at a time and append them to a buffer -- performing your check each time. But either strategy is logically equivalent.
I also recommend just using the InputStreamReader. It has various read() methods. I am a bit suspicious about the BufferedInputReader, especially when dealing with data that isn't newline terminated. I'm probably just paranoid. I've just never used it when writing TCP client/server programs, so I'm not sure.

Is it a good way to call AsyncTask in infinity loop to pull data from server?

Well in app I'm trying to pull the data from sever for every 4 sec,and update the app.
I'm using handler,in that I'm calling AsynTask to fetch the data from server for every 4 sec.
Just I'm worried about the instance created for AsynTask every 4'sec causes any problem ?
This is what I'm doing.
private static final int DELAY = 1000 * 4;
final Handler printHandler = new Handler();
private boolean keepLooping = true;
printHandler.postDelayed(printStuff, DELAY);
Runnable printStuff = new Runnable(){
#Override
public void run(){
// call AsynTask to perform network operation on separate thread
new DownloadMainScore().execute("http://server/root/score.php");
if(keepLooping)
printHandler.postDelayed(this, DELAY);
}
};
On your choice of concurrency tool:
You are right that this is not so good. AsyncTasks are designed to be useful helpers when designing occasional asynchronous calls that then need to update a UI. As such, in old (< 1.6) versions of Android the maximum thread pool size was 10!
It would be better to go straight to the very robust Java out of which AsyncTask is built. Given you want to do this repeatedly, try a ScheduledExecutorService. I see they've even made a nice example for you.
Or, given that you seem to be getting a score down, best might be to maintain a persistent connection over a protocol like XMPP, for which there are many Java server and clients.
Finally, you might like to look at gcm.
On design issues in general
I see you want to print a score frequently. Once every four seconds in fact. But what's the point is the score hasn't changed? Furthermore, what if you've got a slow internet connection, and eight seconds later the one for four seconds ago hasn't finished? Right now you will set off yet another download request, even though the other one when it comes back will be up to date!
The solution is to decouple the download mechanism and the UI update mechanism. One way to do it is to have your scheduled download on a single threaded executor- not something you can control in an AsyncTask, which when finishes causes the UI to update and show the score.
Wishing you the best of luck!
Code sketch
Don't have environment set up right now, but in a very rough code sketch (check syntax), using a scheduled executor would look like:
In class:
private final ScheduledExecutorService downloadScheduler = Executors.newSingleThreadScheduledExecutor(1);
Then elsewhere, wherever you start doing this
final Runnable scoreHttpRunnable = new Runnable() {
#Override public void run() {
...
//do Http Syncronously here- I guess whatever is in the doInBackground(...) part of that Async task you wrote!
...
final int newScoreResult = ... (do whatever you do here)
...
runOnUiThread(new Runnable() { #Override public void run() { yourView.updateHoweverYouLike(newScoreResult); } })
...
};
downloadScheduler.scheduleAtFixedRate(scoreHttpRunnable, 0, 4, TimeUnit.SECONDS);
Going one of the other two routes is really too much to post in a single answer to a question. That'd be a another SO question if there isn't already one.
Be sure that next call send to asyc class only after once its done for that make a variable(IsLoadRunning) and make it true in on preExecute() and false in onPOstExecute and add a condition if(!IsLoadRunning){new DownloadMainScore().execute();}
As official documentation states
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
Services can serve better in you case. Have a look at the accepted answer here
#Override
protected String doInBackground(String... params) {
Log.d(TAG, "type - " + params[0] + ", url = " + params[1] + ", name = " + params[2]);
downloadFile(params[1], params[2]);
return null;
}
here is download method
URL url = new URI(Url.replace(" ", "%20")).toURL();
URLConnection connection = url.openConnection();
connection.setConnectTimeout(1000);
int fileLength = connection.getContentLength();
mSavePath = CommonUtilities.getFileSavePath(mContext, fileName, fileLength);
Log.d(TAG, "*** saveFilePath - " + mSavePath);
InputStream inputStream = connection.getInputStream();
if (inputStream != null) {
File file = new File(mSavePath);
BufferedOutputStream bufferOutputStream = new BufferedOutputStream(new FileOutputStream(file));
byte byteArray[] = new byte[1024];
int len = 0;
long total = 0;
while ((len = inputStream.read(byteArray)) != -1) {
bufferOutputStream.write(byteArray, 0, len);
total += len;
}
bufferOutputStream.flush();
bufferOutputStream.close();
inputStream.close();
} else {
Log.d(TAG, "*** inputStream is null");
}

Android String Parsing

I am writing an android app that recieves data over bluetooth. The bytes comming in can be of any size example: 00023>024935928598235>9284>
As you can see each set is seperated by ">". The data comes in extremely fast. I would like some ideas for an implementation. See my problem is that I need to read the data into a byte array that can and then convert it to a string and split them according to the delimeter of ">".
so in the above example:
00023
024935928598235
9284
If i set byte[] data = new byte[8] then when reading the incomming data it might get 00023>02 which is not what i want. I'm not sure how to implement something like this. Any ideas?
Here's one approach. You'll have to implement the readDataFromBluetooth() and somehow set dataAvailable, but this should get you on the right track.
byte[] data = new byte[1024];
List<String> chunks = new LinkedList<String>();
StringBuilder chunk = new StringBuilder();
while (dataAvailable) {
data = readDataFromBluetooth();
for (byte b : data) {
if (b == '<') {
chunks.add(chunk.toString());
chunk.setLength(0);
} else {
chunk.append(b);
}
}
}
if (chunk.length() > 0)
chunks.add(chunk.toString());
I would recommend using a buffered stream, but maybe a bit bigger that 8 bytes, as you suggest, and the read one and one character from the beginning of the stream, accumulating the string. When you encounter a ">", send the value you have accumulated off to a queue for a background thread processing. Use standard producer/consumer implementation techniques (e.g. the Monitor pattern) to communicate via the queue.

Categories

Resources