Android socket implementation with AsyncTask force close - java

I'm facing with an issue when try to send 'signal' to my AsyncTask class to stop execution and close socket connection. In doInBackground method I setting up socket connection, sending first payload packet and waiting for incoming packets:
mRunning = true;
try {
byte[] data = null;
mSocket = mTlsSocketFactory.createSocket(mTlsSocketFactory.getServerAddress(), mTlsSocketFactory.getServerPort());
LogHelper.printLogMsg("INFO socket created");
out = new DataOutputStream(mSocket.getOutputStream());
out.flush();
inStream = new DataInputStream(mSocket.getInputStream());
//send authenticate payload
requestSendPayload(authenticatePayload, params);
while (mRunning) {
int type = inStream.readInt();
type = Integer.reverseBytes(type);
mResID = type;
int length = inStream.readInt();
length = Integer.reverseBytes(length);
if (length > 0) {
data = new byte[length];
inStream.readFully(data);
publishProgress(data);
}
data = null;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) out.close();
if (inStream != null) inStream.close();
if (mSocket != null) mSocket.close();
} catch (IOException e) {
e.fillInStackTrace();
}
}
When I receive packet that I want, I should close connection. I have a public method inside AsyncTask class:
public void close() {
mRunning = false;
}
But the problem is that 'while' block never ends and doInBackground never finished.
There is a lot of posts with similar problem but I tried to call cancel(true) on my AsyncTask but with no result - doInBackground never finished. My question is how to send 'signal' to doInBackground method so that my while loop be able to finish?
I can rewrite closeMethod to something like this:
new Thread(new Runnable() {
#Override
public void run() {
try {
if (out != null) out.close();
if (inStream != null) inStream.close();
if (mSocket != null) mSocket.close();
} catch (IOException e) {
e.fillInStackTrace();
}
}
}).start();
And after that, I catch exception when try to read: DataInputStream.readInt() and then doInBackground will end. But I'm not sure if this is the correct solution.

You should implement OnCancelled in your async task method
#Override
protected void onCancelled() {
mRunning = false;
}
after that you just call
mySocketAsyncTaskObject.cancel(false);
Another option would be to remove your mRunning property and OnCancelled override and just check for IsCancelled()
while (!IsCancelled()) {
int type = inStream.readInt();
type = Integer.reverseBytes(type);
mResID = type;
int length = inStream.readInt();
length = Integer.reverseBytes(length);
if (length > 0) {
data = new byte[length];
inStream.readFully(data);
publishProgress(data);
}
data = null;
}
As a personal note, i want to add, that it is good practice to use Service class for incapsulation your Socket functionality and use plain Thread object not AsyncTask

Related

Send messages to Handler from other activity

I Have a code that connects to a bluetooth device, opens a bluetooth socket that communicates with a running thread which operates functions running in main activity.
I would like to move all the connecting sequence to another activity, and then operate the thread from the main one as done now. The problem is they are all connected.
I would like to have the option of sending a message between these activities(meaning remaining the socket operating from the other activity), i.e this message:
mHandler.obtainMessage(CONNECTING_STATUS, 1, -1, name)
.sendToTarget();
because it is impossible to pass handler between activities I don't know how/if possible to do so.
What is the best way of doing such a thing?
added part of the code.
Thanks.
mHandler = new Handler(){
public void handleMessage(android.os.Message msg){
if(msg.what == MESSAGE_READ){
String readMessage = null;
try {
readMessage = new String((byte[]) msg.obj, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
RxMessage = readMessage.split(" ");
if (sH.isStringInCorrectOrder(RxMessage,Weight))
populateListView(RxMessage);
mReadBuffer.setText(readMessage);
}
if(msg.what == CONNECTING_STATUS){
if(msg.arg1 == 1)
mBluetoothStatus.setText("Connected to Device: " + (String)(msg.obj));
else
mBluetoothStatus.setText("Connection Failed");
}
}
};
private void connectBT (){
mBluetoothStatus.setText("Connecting...");
// Get the device MAC address, which is the last 17 chars in the View
final String address = "98:D3:31:30:39:75";
final String name = "HC-06";
// Spawn a new thread to avoid blocking the GUI one
new Thread()
{
public void run() {
boolean fail = false;
BluetoothDevice device = mBTAdapter.getRemoteDevice(address);
try {
mBTSocket = createBluetoothSocket(device);
} catch (IOException e) {
fail = true;
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
// Establish the Bluetooth socket connection.
try {
mBTSocket.connect();
} catch (IOException e) {
try {
fail = true;
mBTSocket.close();
mHandler.obtainMessage(CONNECTING_STATUS, -1, -1)
.sendToTarget();
} catch (IOException e2) {
//insert code to deal with this
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
}
}
if(fail == false) {
mConnectedThread = new ConnectedThread(mBTSocket);
mConnectedThread.start();
mHandler.obtainMessage(CONNECTING_STATUS, 1, -1, name)
.sendToTarget();
}
}
}.start();
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.available();
if(bytes != 0) {
SystemClock.sleep(100); //pause and wait for rest of data. Adjust this depending on your sending speed.
bytes = mmInStream.available(); // how many bytes are ready to be read?
bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget(); // Send the obtained bytes to the UI activity
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(String input) {
byte[] bytes = input.getBytes(); //converts entered String into bytes
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
Just declare mHandler as static and you can access it from all other activities. This will create a small temporary memory leak, but don't worry about it.

changing from runnable to async task

I've got an app reading from a socket. I've tested this on a phone with API 18 on. If I try it on a phone with API 24 it throws a NetworkOnMainThread exception.
I've read that I need to do this in AsyncTask for the later API's, or I'm doing something else wrong. I'm unsure what I need to do with my code to convert it to an AsyncTask for the way I'm waiting for input stream data?
This is my read input stream class.
class receiveData extends Thread {
private volatile boolean exit = false;
DataInputStream in;
byte[] fullBuffer = new byte[7];
byte[] buffer = new byte[100];
int bytes; // bytes returned from read()
int bytesCount = 0;
public void run(){
try {
if (socket.isConnected()) {
in = new DataInputStream(socket.getInputStream());
}
} catch(Exception e) {
Log.d(TAG, "in receiveData - run exception - " + e.toString());
}
while(!exit) {
try {
bytes = in.read(buffer);
System.arraycopy(buffer, 0, fullBuffer, bytesCount, bytes);
bytesCount = bytesCount + bytes;
if (bytesCount >= 7) {
h.obtainMessage(NOW_DATA_RECEIVED, bytesCount, -1, fullBuffer).sendToTarget(); // Send to message queue Handler
bytesCount = 0;
} else {
Log.d(TAG, "Receive Error");
bytesCount = 0;
}
} catch(Exception e) {
Log.d(TAG, "Read Error - " + e.toString());
}
}
}
public void stopRdThread() {
exit = true;
try {
socket.close();
} catch(Exception e) {
Log.d(TAG, "error closing socket - " + e.toString());
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] message) {
try {
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(message);
out.flush();
} catch (IOException e) {
Log.d(TAG, "...Error data send: " + e.getMessage() + "...");
}
}
}
I'm starting it like this.
private receiveData rd;
// start receiver thread
rd = new receiveData();
rd.start();
Like I said it's working great in API 18 but won't in API 24. All the examples I've seen are for one off tasks in the background, such as downloading a picture, not threads that are left running waiting for data.
Well all newest androids API's will throw exceptions when you try connect network in ui thread.
The only way is to use AsyncTask or use Handler;
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
//Do something
}
});
Note: If you want to use network and then display things on UI then you must use AsyncTask.

Can't detect the socket is disconnected [duplicate]

This question already has answers here:
JAVA : Handling socket disconnection
(4 answers)
Closed 5 years ago.
Hi this is my code for sending data through a socket to another device connected to the network
try {
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
printWriter.print(data);
Log.d("error", printWriter.checkError() + "");
printWriter.flush();
} catch (IOException e) {
Log.d("socket","is disconnected");
e.printStackTrace();
}
the problem is printWriter.checkError() is always returning false and the IOException never happens. for disconnecting socket I'm turning device off and trying to send data again. even reading from InputStream doesn't help
private class SocketHandler extends AsyncTask<Void, byte[], Void> {
InputStream in;
#Override
protected void onPreExecute() {
try {
in = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
byte[] content = new byte[2048];
if (in != null) {
while (true) {
try {
if (in.available() > 0) {
int n = in.read(content);
if (n == -1) {
break;
}
publishProgress(new byte[][]{Arrays.copyOfRange(content, 0, n)});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
#Override
protected void onProgressUpdate(byte[]... values) {
String data = new String(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Void aVoid) {
Log.d("socket", "is disconnected");
}
}
read never returns -1 so I can detect the socket is disconnected. What can I do?
edit: It's not duplicate of JAVA : Handling socket disconnection because I did everything mentioned there
I had the same problem. I used a timer to send connection check command every second in both side and in that timer I checked the last time that I received this connection check command and if it's over for example 10 seconds, then I decided that the socket is disconnected
tOut = new Timer();
tOut.schedule(new TimerTask() {
#Override
public void run() {
long dif = System.currentTimeMillis() - lastCCReceivedTime;
if (dif > 1000 * 10) {
// socket is disconnected
return;
}
try {
out.println("Connection check");
} catch (Exception e) {
if (out != null)
out.close();
// socket is disconnected
}
}
}, 1000, 1000);
Save the last time that the command was received
while ((msg = in.readLine()) != null) {
lastCCReceivedTime = System.currentTimeMillis();
//Message received
}
if (in.available() > 0) {
You never call read() unless there is data available to be read without blocking.
int n = in.read(content);
if (n == -1) {
break;
Unreachable.
}
And here if available() was zero you do nothing except spin mindlessly smoking the CPU.
Cure: remove the available() test. It isn't doing anything useful, and it is preventing you from detecting end of stream.

InputStream is not working properly

I am a beginner in android. I am trying to work on Sockets. But my InputStream is not reading the data as expected. It is getting out of the method after j = inputStream.read(arrayOfByte, 0, i); Please help me.
public void readinputstreamforid(final String ip, final int port){
AsyncTask asyncTask = new AsyncTask() {
#Override
protected Object doInBackground(Object[] objects) {
try {
socket=new Socket(ip,port);
} catch (IOException e) {
e.printStackTrace();
}
final byte[] arrayOfByte = new byte[10000];
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
while (socket.isConnected()) {
int j = 0;
int i = arrayOfByte.length;
try {
j = inputStream.read(arrayOfByte, 0, i);
if (j == -1)
throw new IOException("not working");
if (j == 0)
continue;
} catch (IOException e) {
e.printStackTrace();
}
final String strData = new String(arrayOfByte, 0, j).replace("\r", "").replace("\n", "");
Log.d("hello","recieved: "+strData);
}
try {
IOUtils.write("!##\n",socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
asyncTask.execute();
}
If an error happens, you are logging it, but then you continue with the code, where more errors can then happen. When an error happens, STOP looping and exit the function. InputStream.read() returns -1 when the end of the stream is reached. For a socket, that means when the connection is closed. That is not really an error condition, so you don't need to throw an exception. Just break the loop. You can wrap the InputStream inside of a BufferedReader so you can use its readLine() method instead of reading bytes manually.
Also, you are trying to write to the socket's OutputStream after the socket has already disconnected. That will never work.
Try something more like this:
public void readinputstreamforid(final String ip, final int port){
AsyncTask asyncTask = new AsyncTask() {
#Override
protected Object doInBackground(Object[] objects) {
try {
socket = new Socket(ip, port);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
OutputDataStream out = socket.getOutputStream();
do {
String data = in.readLine();
if (data == null)
break;
Log.d("hello", data);
IOUtils.write("!##\n", out, StandardCharsets.UTF_8);
}
while (true);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
asyncTask.execute();
}

Input Stream getting filled with null data

So I am creating a server that I am trying to get to handle ASCII data. While I can get the Streams to work and call the methods. However the listening thread to add items to a Queue (ArrayBlockingQueue), and it will loop until the queue is full with null data.
Server code, Client Handler (compressed, let me know if I left something out.):
class ClientThread extends Thread {
// ASCII commands defined here (byte NUL=0x00; byte SOH=0x01; etc.)
private Socket socket;
private InputStream sInput;
private OutputStream sOutput;
BlockingQueue<byte[]> queueIn = new ArrayBlockingQueue<>(30, true);
private boolean goodToGo = false;
ClientThread(Socket socket){
id = ++Server.uniqueId; /* To be replaced with UIDs */
this.socket = socket;
/* Create Data Streams */
try {
sInput = (socket.getInputStream());
sOutput= (socket.getOutputStream());
goodToGo = true;
} catch (IOException ex) {
ServerInit.logger.log(Level.WARNING, "Error Openning Streams!", ex);
}
}
#Override
public void run(){
boolean keepGoing = true;
System.out.println("Client thread started.");
/* Start listening thread */
new Thread() {
#Override
public void run(){
while(goodToGo) {
System.out.println("Listening thread looping.");
try {
byte[] temp = IOUtils.toByteArray(sInput); // read client input using Apache Commons IO.
// Add the result to the queue.
queueIn.put(temp);
} catch (EOFException eof){
ServerInit.logger.log(Level.INFO,"Remote client closed connection.");
close();
}
catch (IOException ex) {
ServerInit.logger.log(Level.WARNING, "Error Reading Stream!", ex);
close();
}
}
}
}.start();
while (keepGoing && goodToGo){
System.out.println("Main thread looping.");
try{
byte[] message = queueIn.take();
if (message.length >= 4){
/* Message picked apart and worked with here */
} else if (message.length == 0 ){
// Do nothing.
} else {
ServerInit.logger.log(Level.WARNING, "Unable to process item from queue.");
}
} catch (Exception e) {
/* Here just for completeness, I don't catch Exceptions this way. :) */
}
}
}
protected void close(){
// try to close the conection
goodToGo = false;
try {
if (sOutput != null) {
sOutput.close();
}
if (sInput != null) {
sInput.close();
}
if (socket != null) {
socket.close();
}
ServerInit.SERVER.remove(id);
} catch (Exception e){
ServerInit.logger.log(Level.FINER, "Error closing client connections.", e);
}
}
}
And client code:
public class TestClient{
public static void main(String args[]){
try{
Socket socket = new Socket("localhost", 5525);
OutputStream outputStream = socket.getOutputStream();
byte[] buffer = { 0x02, 0x05, 0x07, 0x04 };
outputStream.write(buffer);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
/* Again, I don't catch exceptions like normally. */
}
}
}
My Questions: What is causing the "listening" thread to loop and add null data to queue indefinitely?
And While I know this is not the Code Review exchange, if anyone can think of better classes to utilize, If they could just mention it.
EDIT:
Following a suggestion, I changed the queue from an ArrayList<> to an ArrayBlockingQueue.
IOUtils.toByteArray() is not appropriate for this usage. It will read to end of stream and return you one big byte array, not a sequence of messages. So there is certainly no point in calling it twice, or in a loop. After the initial result, all you can get is an infinity of empty byte arrays.
I've not used IOUtils.toByteArray but my suspicion is that if there is no data in the stream when you call it then it either returns null or an empty array.
This makes sense if you think about it since otherwise it has no idea how many bytes to read. It has no way to know if you are sending an array containing 1, 4 or 1000 bytes so it just reads everything that is ready when you call it.
You need to somehow sleep between each call to toByteArray and ignore any empty responses. A better way would be to see if you can sleep until more data arrives on the socket.

Categories

Resources