Android sockets: receiving data in real-time - java

I have another device & application transmitting data in real-time (every few ms) and on my receiving device, I want to:
1) read/receive this data, and
2) use it to update a UI element (a dynamic graph in this case)
The data sender uses a socket in a background service, using AsyncTasks every few ms to send data. To initialize, it does the following:
echoSocket = new Socket(HOST, PORT);
out = new PrintWriter(echoSocket.getOutputStream(), true);
And to periodically send data it does:
static class sendDataTask extends AsyncTask<Float, Void, Void> {
#Override
protected Void doInBackground(Float... params) {
try {
JSONObject j = new JSONObject();
j.put("x", params[0]);
j.put("y", params[1]);
j.put("z", params[2]);
String jString = j.toString();
out.println(jString);
} catch (Exception e) {
Log.e("sendDataTask", e.toString());
}
return null;
}
}
How should I go about receiving this data in my application? Should I also use a background service with AsyncTasks that try to read from the socket every few ms? How about communicating with the UI thread?

There are many ways to do this. The simplest would be to use blocking reads in the doInBackground method of an AsyncTask and call publishProgress() to forward the new data to the UI thread.
Then implement onProgressUpdate with code (running in the UI thread) that updates the screen.
You should be aware that your read may not receive the entire message that you sent -- you may need to read more data and append it to the input received so far until you have an entire JSON message.
By blocking reads, I mean something like this (in pseudo code):
open a socket connected to the sender
is = socket.getInputStream()
initialize buffer, offset, and length
while the socket is good
bytesRead = is.read(buffer, offset, length)
if(bytesRead <= 0)
bail out you have an error
offset += bytesRead;
length -= bytesRead
if(you have a complete message)
copy the message out of the buffer (or parse the message here into
some other data structure)
publishProgress(the message)
reset buffer offset and length for the next message.
(remember you may have received part of the following message)
end-if
end-while
The copy-out-of-the-buffer is necessary because the onProgressUpdate does not happen immediately so you need to be sure the next message does not overwrite the current one before it gets handled.

Related

Java writing to closed output stream not throwing ioexception

Here is the flow of my client/server.
Socket is created in main thread.
Socket passes to Thread 1.
Client sends data to server
Server responds to client
Server closes input stream, output stream, and socket by invoking close()
Socket returned to main thread, and then passed to Thread 2.
Client writes data to server - no exception, no errors, server gets no data
Client attempts to read data - no exceptions, no errors
How can I detect the problem that socket was closed?
try {
os = new DataOutputStream(this.socket.getOutputStream());
os.write(data);
os.flush();
System.out.println("Written " + data.length + " bytes");
} catch (IOException e) {
System.out.println("Client failed to write to stream");
System.out.println(e.getMessage());
e.printStackTrace();
}
The exception is never thrown. It says Written 60 bytes. Any ideas?
UPDATE
Here is the way that I read the response. I wait for data, read first 4 bytes (which gives the the length of response), and keep reading until I read the specified length. This loop never ends because no data ever comes in.
is = new DataInputStream(this.socket.getInputStream());
while(true){
while(is.available() > 0){
bos.write(is.read());
}
if(contentLength == 0 && bos.size() > 3){
byte[] bytes = bos.toByteArray();
byte[] size = Arrays.copyOf(bytes, 4);
contentLength = ByteBuffer.wrap(size).getInt();
}
if(bos.size() - 4 < contentLength){
continue;
}
break;
}
When the server closes its end of the TCP connection, it sends a FIN packet to tell the client. The client program sees this as the InputStream reaching the end. Also, if the client tries to write the server will send an RST packet to signal error. If the client program tries to write after RST was received, the API will throw a SocketException with the message "connection reset."
Your problem is with detecting the end of the input here:
while(is.available() > 0){
bos.write(is.read());
}
The available method probably doesn't do what you think it does. If you want to read 4 bytes, read 4 bytes:
byte[] bytes = new byte[4];
is.readFully(bytes); // throws EOFException on end of input
It says that it wrote 60 bytes because it did. The server just isn't listening anymore. You need to get the input stream to see if anything is there. If nothing comes back, then you know that the connection didn't work.
UPDATE
Use a timer to determine if the server is responding.
Long currenttime = System.currentTimeMillis();
while(System.currentTimeMillis() - currentTime < x){ //x = miliseconds you want to wait
(try to read from server)
}
This way if it takes longer than x seconds, it will time out and you can do some error catching after the while loop

Receiving real-time GPS data via UDP

I'm building a real-time GPS tracking system, which will receive GPS data sent from a couple of Arduino devices using UDP. I have this code so far:
PreparedStatement stmt ...
DatagramSocket serverSocket = new DatagramSocket(9876);
byte[] receiveData = new byte[1024];
while(true){
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
String received = new String( receivePacket.getData());
System.out.println("RECEIVED: " + received);
stmt.set...
stmt.execute();
}
1 - Anyone with more knowledge could tell me if there's a better way of doing this? I really don't know how the JVM handles this, but I don't like that infinite loop.
2 - Lets say that I have 50 Arduinos sending data. I need to use threads or something like this?
3 - It's best to use a thread per "connection" (UDP is connectionless) like an answer below or use frameworks/libs like Apache Mina or Netty?
There is no problem using an infinite loop in this case. Calling receive waits until a new datagram is delivered:
This method blocks until a datagram is received. T...
So no CPU power is wasted here, it simply waits until new data is available.
If you have many clients or if processing the packet isn't completely trivial, you should start a new thread for processing each one, so that the main thread that receives the datagrams doesn't get blocked. Probably the best approach is to use thread pools that will create threads for you, and at the same time prevent creating too many threads if your application is overloaded by requests.
I'd proceed as follows:
Create a dedicated thread for receiving the datagrams. It could also create a thread pool for dispatching processing the requests. Something like:
int maxNumberOfThreads = ...; // your choice
int bufSize = ...; // your choice
ExecutorService exec = Executors.newFixedThreadPool(maxNumberOfThreads);
DatagramSocket serverSocket = new DatagramSocket(9876);
while (true) {
// we need to create a new buffer every time because
// multiple threads will be working with the data
DatagramPacket receivePacket =
new DatagramPacket(new byte[bufSize], bufSize);
serverSocket.receive(receivePacket);
exec.submit(new YourTask(receivePacket));
}
Create class YourTask that processes the datagrams:
// We don't use return values for anything here, so
// we just use Object.
public class YourTask extends Callable<Object> {
private DatagramPacket received;
public YourTask(DatagramPacket received) {
this.received = received;
}
public Object call() {
// do your processing here
System.out.println("RECEIVED from " +
received.getAddress() +
": " + new String(received.getData(),
0, received.getLength()));
return null;
}
}
I recommend you look at Apache MINA (http://mina.apache.org/), a great framework for network applications. With MINA you don't need to implement a loop or worry about threading.
The actual problem I see in your question is the "Real Time" term. What do you mean with that? Do you need a highly predictable (in terms of timing) application, is it safety/mission critical? If so there may be problem in using Java, as it is for many reasons (i.e. garbage collector, etc.) not realtime. There are however some realtime JVM as http://www.atego.com/products/aonix-perc/.
I do like Java but I guess in this case, if you really need a RT system, C++ would be a better choice.

Incomplete UDP Packet received

I'm facing a very weird problem with receiving data using UDP in Android.
I'm writing an application to control a wifi module from an android device. I'm able to successfully send data to this remote wifi device. But I'm not able to receive 'complete' data packet from this wifi device.
My code in android is:
public static void receivePacket(int receiverPort, Context context) {
DatagramSocket socket = null;
String text = "";
try {
byte[] message = new byte[1500];
DatagramPacket packet = new DatagramPacket(message, message.length);
socket = new DatagramSocket(receiverPort);
//socket.setSoTimeout(5000);
for (int i = 0; i < 12; i++) {
socket.receive(packet);
text += new String(message, 0, packet.getLength()) + "\n";
}
socket.close();
Log.d("Received Message", text);
} catch (Exception e) {
Log.e("UDP", "S: Error", e);
} finally {
if(null != socket){
socket.close();
}
}
}
So if I'm expecting the data "$BEG1;$PID2;$PIP192.168.15.245;$PPN80;$DAT987654321;$END1;" I'm only getting "$BEG1;$PID2;$PIP192.168.15.245;$PPN80;$DAT98"
I tried to use UDP WinChat application to see if it's able to get the message from the wifi module and I'm able to get the entire data.
Also if i try sending a really long message to the android device using UDP Win Chat Application I'm able to get the entire data!
I'm totally confused! Please Help.
I was able to isolate the problem. (Still havent found the fix though :(...)
From the above code I'm making use of the same packet.getLength() for every iteration assuming that it will change each time according to the data it has received. But sadly that's not the expected behavior. The getLength() makes use of the previous value and truncates the newly arrived messages.
[Please note: This is a random behavior and doesn't happen all the time]
Now the question is, how do I change or refresh this attribute everytime I receive a new message within the loop?
You need to reset the DatagramPacket length before every receive. Otherwise it keeps shrinking to the smallest packet received so far.

Java TCP server - android client packet loss

I'm trying to create a basic multiplayer game for android, using a Java TCP server and Android client. The problem is slow speed when sending TCP packets. When I put Thread.sleep(100) then it works.
server side:
for(int i = 0; i<50; i++) {
client.send("test_" + i);
}
client just received (~3 packet)
test_0
test_1
server with sleep:
for(int i = 0; i<50; i++) {
client.send("test_" + i);
Thread.sleep(100);
}
client received ~45
EDIT: client side:
while (true) {
if (!running)
break;
inFromServer = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"), 2 * 1024);
String rawRecervied = inFromServer.readLine();
if (rawRecervied == null) {
close();
break;
}
final String recervied = rawRecervied.substring(2); // for skip utf bom chars
new Thread() {
public void run() {
listener.dataRecervied(recervied);
Log.e("TCP recervied", recervied); // debug
}
}.start();
}
Maybe the key is in the BufferedReader. You're in a loop, and constantly create a BufferedReader to check if something has been sent from the server. Once data is detected, you start processing it, but data keeps coming, and is buffered in the BufferedReader. After processing the initially detected data, you create again a BufferedReader but, what happens with all the data that was already buffered in the BufferedReader created before? Maybe it's lost.
Could you try creating the BufferedReader outside the loop?
If it is a one-way protocol where packet loss is acceptable then use UDP instead of TCP as it is cheaper in terms of network resources. I think this is not your case however. If TCP, then implement a basic flow control where the client acknowledges the received packet with echoing its ID back to the server.
You should also revise your client and server code because this behaviour might be in the way you implemented that client.sent(..). Do you always close and reopen the connection? Or what?

Java threading issue with handler message data being overwritten by next message

I have a thread reading data from a bluetooth stream that sends the data to a handler on the main UIThread as it comes in (based on the Bluetooth Chat Sample).
I've discovered a threading problem that pops up quite frequently. First, some code for reference.
BluetoothService.java (Just the part that reads the incomming data stream. It has been set up correctly before this code runs).
public void run() {
DebugLog.i("BluetoothService", "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
DebugLog.d("BluetoothService", new String(buffer, 0, bytes));
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
DebugLog.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
Handler defined in my main activity (partial):
// The Handler that gets information back from the BluetoothService
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BluetoothService.MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
DebugLog.d("BluetoothHandler", readMessage);
mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
break;
}
}
};
My problem comes when a small, continuous stream of data comes into the mmInStream. For example, 'abcd'. If 'abcd' is read all at once, this code functions normally and the log reads:
BluetoothService: BEGIN mConnectedThread
BluetoothService: abcd
BluetoothHandler: abcd
But if that continous block of data gets read in 2 parts, the second set of data overwrites the first set of data by the time it gets to the handler. Here's some example logs that I've seen.
BluetoothService: BEGIN mConnectedThread
BluetoothService: a
BluetoothService: bcd
BluetoothHandler: b
BluetoothHandler: bcd
Or:
BluetoothService: BEGIN mConnectedThread
BluetoothService: abc
BluetoothService: d
BluetoothHandler: dbc
BluetoothHandler: d
Or:
BluetoothService: BEGIN mConnectedThread
BluetoothService: ab
BluetoothService: cde
BluetoothHandler: cd
BluetoothHandler: cde
Notice that the second message that gets sent always overwrites the first message's data and only up to the shortest message's length. Also, both messages are always sent before the first message has been processed by mHandler.
I am guessing that I'm a common buffer somewhere is getting overwritten before the the first message is fully processed, but I don't see where. Any suggestions?
Is it possible that you have to use a second byte buffer when creating the message object?
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, copyOfBuffer)
I have a suspicion that mHandler (although I don't know what it is) is keeping a reference to that byte array you send him.
I realize this is an OLD thread, but for posterity...
I just ran across this same issue. Coincidentally, it was also in code based on the google Bluetooth chat sample. My code also talks to a bluetooth device whose data appears in 'snippets' in the mmInStream.read(), resulting in many small messages sent to the handler.
Like the original poster, I found that the messages were being overwritten. I believe this is due to the android implementation of message handling as a light weight messaging mechanism. Specifically, .obtainMessage() allocates from a global pool of message structures to avoid runtime allocations. In keeping with this requirement, they DO NOT COPY THE DATA (/OBJECT) contained within the message, but simply maintain a pointer to the original data. Each message object contains the count of bytes in msg.arg1. In the sample code, if the data within the byte array ('buffer') is changed before the receiving handler has processed the message, the handler still has the original size of the message (contained in msg.arg1), but the buffer data has been updated (/overwritten).
I can see three ways to address this issue:
1. Use the android recommended mechanism (create a bundle of data, and use .setdata() to attach this to the message). I've not tried this, but I expect the bundle creation will result in the data being copied out of the buffer byte array.
2. use new memory for message data. This can be by run-time allocating (but that conflicts with the 'light weight' intention of messaging), or by using multiple statically allocated buffers, and cycling between them. Either approach has issues.
3. perform collection in the low-level thread 'run()', and only send a message for completed Bluetooth messages.
I opted for the third method. In my case, bluetooth messages contain terminated strings, so I use a string builder to collect bytes returned by mmInStream.read(), and only send a message to the handler when the end of line is detected.
A different way is to check if there is already any message with the same name in the Handler's queue with .hasMessages(). Example:
public void run() {
DebugLog.i("BluetoothService", "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
//Check if there is no pending similar message on the queue
if (!mHandler.hasMessages(Constants.MESSAGE_READ)) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
DebugLog.d("BluetoothService", new String(buffer, 0, bytes));
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
DebugLog.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
}

Categories

Resources