Android: BluetoothSocket receives its own output - java

I have an app that connects to a RaspberryPi via Bluetooth and loops the same data to it while it receives some data back.
I had some issues with the connection so this workaround is needed to connect my android phone to the RaspberryPi: IOException: read failed, socket might be closed - Bluetooth on Android 4.3
For some reason, the android phone is receiving its own output.
The String "Hello Raspberry. It's me, AndroidPhone" is sent to the output in a never-ending loop. The incoming data (from the RaspberryPi) is also read in a never-ending loop.
But somehow I don't only receive the data from the RaspberryPi but also the string sends via smartphone. This is my code:
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter bluetoothAdapter;
UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
#Override
protected void onCreate(Bundle savedInstanceState) {
// (...)
// Only GUI-stuff until this point
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice raspberryPi = bluetoothAdapter.getRemoteDevice("B8:27:EB:56:DC:B2");
BluetoothSocket btSocket;
try {
btSocket = raspberryPi.createRfcommSocketToServiceRecord(SERIAL_UUID);
btSocket.connect();
} catch (IOException e) {
Log.e("BTError", e.getMessage());
// Workaround, found on: https://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3
try {
Log.e("BTError", "Trying fallback...");
btSocket = (BluetoothSocket) raspberryPi.getClass().getMethod("createRfcommSocket", new Class[]{int.class}).invoke(raspberryPi, 1);
btSocket.connect();
(new Thread(new SendingThread(btSocket))).start();
(new Thread(new ReceivingThread(btSocket))).start();
} catch (Exception e2) {
Log.e("BTError", e2.getMessage());
Log.e("BTError", "Couldn't establish Bluetooth connection!");
}
}
}
private class SendingThread extends Thread {
private OutputStream out;
public SendingThread(BluetoothSocket btSocket) {
try {
out = btSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
try {
int delay = 100000000;
while (true) {
if (delay == 0) {
Log.i("WRT", "Written to RaspberryPi");
out.write("Hello Raspberry. It's me, AndroidPhone".getBytes());
delay = 100000000;
}
delay--;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class ReceivingThread extends Thread {
private InputStream in;
public ReceivingThread(BluetoothSocket btSocket) {
try {
in = btSocket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
int data = 0;
while (true) {
try {
data = in.read();
} catch (IOException e) {
e.printStackTrace();
}
Log.i("RCV", String.valueOf((char) data));
}
}
}
On the RaspberryPi end, everything looks normal. A simple java program starts the Linux command rfcomm listen /dev/rfcomm0 and reads from/writes to the file /dev/rfcomm0 with FileReader and FileWriter. The only relevant lines on this end are:
run {
// Inside writer-thread
bluetoothWriter = new BufferedWriter(new FileWriter("/dev/rfcomm0"));
while(true) {
bluetoothWriter.write("This is RaspPi");
bluetoothWriter.flush();
}
}
and
run {
// Inside reader-thread
bluetoothReader = new BufferedReader(new FileReader("/dev/rfcomm0"));
while(true) {
int incData = bluetoothReader.read();
System.out.print((char) incData);
}
}
Thank you for your help!
edit: Still no solution to this problem. I suspected that the RaspberryPi is somehow sending back what it received. But when I disabled that it sends out anything, the smartphone still directly receives what it has sent out.

I scoured over the Bluetooth classes sources. The workaround seems legit from the first glances. Try this first:
if (delay == 0) {
Log.i("WRT", "Written to RaspberryPi");
out.write("Hello Raspberry. It's me, AndroidPhone".getBytes());
out.flush(); // <-- You are not flushing
delay = 100000000;
}
And the message sticks in you socket for you to read over and over again.
If that does not fix it the other option I can think of is that somehow the socket is initialized to be a socket to your Android device. The .createRfcommSocket() method seems to create a socket to your own device if the Bluetooth device is null when the socket was being created. I'm not sure how this would exactly happen, but if the Raspberry Pi's state is somehow mangled after exception I suppose it could be something to look into.
On the raspy side: If you are just starting both of those threads doesn't it mean that you are constantly sending messages to /dev/rfcomm0 and flushing. I recommend that you change it so that raspy reacts to a received message by sending back the wanted message instead of spamming all the time. I'm not sure if this is part of your problem but it would at least make debugging & development a bit easier.

I am not sure if this is the solution you need, because I don't know if you are using bluetooth classic or bluetooth 4.0>+, but I wrote a library for text based BLE and WiFi P2P 2-way communication for android (and I know the Raspberry Pi is capable of BLE communication), I don't create a socket connection for BLE communication though, but I do for WiFi P2P. Take a look, I hope it helps. It isn't published yet, so you would have to clone/fork the repo.

I think you have trouble writing
As far as I know, for buffer, should use \n and ...
bluetoothWriter.write("This is RaspPi\n");
But I prefer to use a combination of DataOutputStream and BufferedReader
For Read:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line = bufferedReader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
for write:
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
String s = "Hi\n";
try {
dataOutputStream.write(s.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
It is better to correct the point made by a dear friend about flush() ...
I'm not sure, please test yourself ...

Related

How do I close any previous Bluetooth sockets after my app starts?

In my app, I open and close a Bluetooth socket on the same device each session and listen for data. When I close one, I make sure to close the input and output streams and then socket in a cancel method. Still, for some people the app keeps trying to read from the device. I know because I read logs that are from run() in the listening thread, where there is a flag for listening that's set to false in cancel, and the listening thread will end when the socket is closed due to an IOException, but this never happens, so the socket must still be opened. I see logs of attempted reads every second of every day even though the person isn't using the app. This might be because the app crashes and the cancel method isn't called. Either way I can't guarantee the cancel method will be called. How do I close any Bluetooth sockets that were previously opened when I start up my app, if these were all opened in new threads created independently?
This guy had the same problem but I didn't see any solution:
Android bluetooth connection doesn't close after application crash
The accepted answer is no good because:
The current users haven't had the UncaughtExceptionHandler run that code yet and they need to have any previous connections closed when the new version is released
The UncaughtExceptionHandler must have a reference to the sockets, which it doesn't have. I want to be able to close any Bluetooth sockets when the app starts.
The guy who made that question asked how to get information about the socket to store for when the app starts up and you want to close them, and got no response.
EDIT:
How I open the socket (removed logging code):
try {
tmp.connect();;
} catch (IOException e) {
isConnected = false;
try {
tmp = (BluetoothSocket) device.getClass().getMethod("createRfcommSocket",
new Class[] {int.class}).invoke(device, 1);
} catch (Exception e2) {
e2.printStackTrace();
}
try {
tmp.connect();
setConnected();
} catch (IOException e1) {
e1.printStackTrace();
isConnected = false;
cancel();
}
How I close the socket:
public void cancel() {
isConnected = false;
listening = false;
try {
if (manageConnection.mmInStream != null) {
manageConnection.mmInStream.close();
manageConnection.mmInStream = null;
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (manageConnection.mmOutStream != null) {
manageConnection.mmOutStream.close();
manageConnection.mmOutStream = null;
}
} catch (IOException e) {
e.printStackTrace();
}
try {
mmSocket.close();
mmSocket = null;
manageConnection = null;
} catch (IOException e) {
// Logging code
}
}
}
Listening:
while (listening == true) {
try {
synchronized (ListeningActivity.lock) {
buffer = new byte[mmInStream.available()];
mmInStream.read(buffer);
....
} catch (IOException e) {
// Code that calls cancel()

Sending objects through sockets java TCP server to android app

I'm writing a client-server pair where the server is a java TCP server running on Linux and the client is an Android app developed in Android Studio.
I've successfully made a client-server pair that sends Message objects to each other, but when I try to implement similar functionality in my Android app nothing seems to happen.
The Android app works while just sending Strings with the readLine() and println() methods from the BufferedReader and Printwriter classes, but not with the readObject() and writeObject() from ObjectOutput / InputStream classes.
Have also tried writeUnshared() / readUnshared() methods without luck.
//Message.java
package Message;
import java.io.*;
public class Message implements Serializable {
String msg;
String tag;
String username;
private static final long serialVersionUID = 4L;
// Methods.
}
// Reading MessageObjects in Server.java.
#Override
public void run() {
Message message = null;
try {
while ((message = (Message)reader.readObject()) != null) {
// Processing message.
}
}
}
// Sending Message Objects in android App.
public void onClick(View v) {
if(!msgBox.getText().toString().equals("")) {
final String msg;
try {
msg = msgBox.getText().toString();
writer.writeObject(new Message(msg, CLIENT, username));
msgBox.setText("");
writer.flush();
// Updating ui etc.
}
catch (IOException e) {
e.printStackTrace();
}
textBox.smoothScrollBy(textBox.getMaxScrollAmount(), 100);
}
}
// Connection-method in android app, initalizes streams.
private boolean connect(String username, String address, int port) {
boolean connected = false;
try {
server = new Socket(address, port);
InputStreamReader(server.getInputStream());
reader = new ObjectInputStream(server.getInputStream());
writer = new ObjectOutputStream(server.getOutputStream());
writer.writeObject(new Message("!newUser",AUTOMATED,username));
writer.flush();
connected = true;
System.out.println("Connected!");
}
catch (Exception ex) {
ex.printStackTrace();
System.out.println("Cannot Connect!");
connected = false;
// UI-things.
}
if(connected){
// Thread that listens for replies.
listenThread();
}
return connected;
}
You need to create the ObjectOutputStream before the ObjectInputStream, at both ends. Otherwise you can get a deadlock.
Your read loop is incorrect. readObject() doesn't return null at end of stream, so using null as a loop condition doesn't make sense. It can return null any time you send a null. The loop should terminate when EOFException is caught.

Android TCP multicast missing message

I build a TCP multicast chat application using asynctask.
I am also trying to order the message in FIFO and causal order.
However, when I try to send a lot of messages simultaneously for testing, it misses some messages but I can't find the reason.
I have tried as hard as I can to improve the performance of the program because I thought the performance could be the reason. but still having the same issue.
I attached some important part of my code.
Most of all,
private class ServerTask extends AsyncTask<ServerSocket, String, Void> {
#Override
protected Void doInBackground(ServerSocket... sockets){
ServerSocket serverSocket = sockets[0];
Socket socket = new Socket();
try {
while(true) {
socket = serverSocket.accept();
InputStream inputstream = socket.getInputStream();
DataInputStream in = new DataInputStream(new BufferedInputStream(inputstream));
String msg = ""+in.readUTF();
String time = ""+in.readUTF();
String temp = time+"||"+msg;
publishProgress(temp);
in.close();
}} catch (IOException e) {
e.printStackTrace();
} finally{
try {
socket.close();
serverSocket.close();////
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
Here is onProgressUpdate.
protected void onProgressUpdate(String...strings) {
/*
* The following code displays what is received in doInBackground().
*/
String strReceived = strings[0].trim();
TextView remoteTextView = (TextView) findViewById(R.id.textView1);
remoteTextView.append(strReceived + "\t\n");
try {
sequencer(strReceived);
} catch (ParseException e) {
e.printStackTrace();
}
return;
}
}
..
private class ClientTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... msgs) {
Date currentDate= new Date();
Timestamp time = new Timestamp(currentDate.getTime());
Message temp = new Message(myPort, msgs[0], time);////
try {
for(int i = 0; i <= 2; i++) {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 0, 2, 2}),
Integer.parseInt(REMOTE_PORTS[i])), 1000);
socket.setTcpNoDelay(true);
OutputStream outputStream = socket.getOutputStream();
DataOutputStream o = new DataOutputStream(new BufferedOutputStream(outputStream));
o.writeUTF(msgs[0]);
o.writeUTF(""+time);
o.flush();////
socket.close();
}
}
catch (UnknownHostException e) {
Log.e(TAG, "ClientTask UnknownHostException");
} catch (IOException e) {
Log.e(TAG, "ClientTask socket IOException");
}
return null;
}
Can you find the part causes the problem?
Sequencing / Queueing /Acknowledgement all these things are part of TCP so it is done by the protocol itself so you do not need to do all those explicitly from your code. There are still some parts of your code that can be improved. Like:
String time = received.split("\\|\\|")[0];
String msgToSend = received.split("\\|\\|")[1];
//Instead of doing this, its better to do this:
String peices[]=received.split("\\|\\|");
String msgToSend=peices[1];
String time=peices[0]
Also you can check if you are receiving all the raw messages and if its during the parsing process the messages are getting lost using a log:
Log.d("RAW_MESSAGE","Message Received: "+temp); //in your doInBackground
If you get all the messages that you send in this log, then there is nothing wrong with the protocol or the sending/receiving process rather there is a problem while you are processing the message. Also for these types of use-cases, try using the Service component rather than AsyncTask.
I hope this helps.
First of all multicast is over UDP, not TCP.
And if you want to create a multicast app, you should use multicastsocket
http://developer.android.com/reference/java/net/MulticastSocket.html

Sockets don't work as excpected with LAN or NET, but perfectly locally

I'm programming a network software with Java, but I have a real problem using my application through a "true" network.
Let a software be a host, and listening for client connexions.
Here is my Server loop :
public void run() {
while (mServerSocket != null) {
try {
Socket wClient = mServerSocket.accept();
System.out.println("Client connecté");
wClient.setSoTimeout(50);
wClient.setTcpNoDelay(false);
Client c = new Client(wClient);
synchronized(this) {
mWaitingClients.add(c);
c.start();
}
} catch(Exception ex) {
System.out.println("Server error : " + ex.getMessage());
}
}
}
When a client tried to connect to the server, I use this function :
public Client connect(InetAddress addr, int port) throws Exception {
Socket socket = new Socket(addr, port);
socket.setSoTimeout(50);
socket.setTcpNoDelay(false);
Client c = new Client(socket);
c.start();
return c;
}
And here is the client loop :
public void run() {
try {
ObjectOutputStream out = new ObjectOutputStream(mSocket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(mSocket.getInputStream());
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
}
out.flush();
mOutMessages.clear();
Thread.sleep(50);
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
Thread.sleep(50);
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
}
Some other parts of the program do Message and put them in the Output list of the Client (mOutMessages).
Some other parts of the program read Message from the mInMessages of the Client.
But something is wrong with this. It works fine locally (server and client on the same computer), but fail or is hazardous (some messages are sent but never received) using two computers (with LAN or through the Internet).
Server ever detect connexions from the clients, send "handshake" messages to the client, but the client never receives them.
I'm more a C programmer than a Java one, and I never had this kind of problem using libc Sockets, so, why my way of doing is wrong ?
Thank you !
Edit :
My Server is created using this function :
public void open(int port) throws Exception {
mServerSocket = new ServerSocket(port);
start(); // Call the run mentionned above.
}
Edit :
Here is my solution, maybe it's not perfect but it works !
public void run() {
try {
BufferedOutputStream buf_out = new BufferedOutputStream(
mSocket.getOutputStream()
);
BufferedInputStream buf_in = new BufferedInputStream(
mSocket.getInputStream()
);
ObjectOutputStream out = new ObjectOutputStream(buf_out);
out.flush();
ObjectInputStream in = new ObjectInputStream(buf_in);
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
out.flush();
}
mOutMessages.clear();
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
If I understand right, both client and server use the run method. If both client and server happen to write sufficiently large messages (not fitting in involved buffers) at the same time then you get a deadlock because neither partner advances to reading (which would drain full buffers). Due to network delays, this might only happen in the non-local scenario, i.e. there may be enough time to pile up enough messages in the mOutMessages buffer.
Note that documentation of Socket.setSoTimeout (which you used) only says that it affects read()s. (For example, in my JDK, ObjectOutputStream seems to use a BlockDataOutputStream with a buffer size of 1024 bytes).
I recommend to either use a separate thread for reading/writing or (if you know the maximum messages size) use a sufficiently large buffer (by wrapping the SocketOutputStream in a BufferedOutputStream). If you opt for larger buffers, you may also want to write one message at a time (and try to read messages after each).

Can't read socket InputStream on Jelly Bean

I have a TCP socket connection which works well on Android 2.3 but now facing some problems on Android 4.1.
The problem is that InputStream.read() method always returns -1 (without blocking), like the connection is closed.
Creating socket:
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket("c.whatsapp.net", 5222);
socket.setSoTimeout(3*60*1000);
socket.setTcpNoDelay(true);
Retrieving input and output streams and writing some initial data:
InputStream inputStream = new BufferedInputStream(socket.getInputStream());
OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
outputStream.write(87);
outputStream.write(65);
outputStream.write(1);
outputStream.write(2);
outputStream.flush();
Then, this condition always passes without blocking:
int c = inputStream.read();
if (c < 0) {
Log.d(TAG, "End of stream");
}
This code is running in a background thread. And it was working on Gingerbread.
Tried to use InputStreamReader and OutputStreamWriter instead of direct streams - no effect.
I have seen that very same error before, although this answer might look offtopic give it a chance and let me know if it worked, for some reason sockets are having strange behavior on jellybean even when they were working completely fine in lower android versions, the way I fixed this issue was to move the targetSdkVersion to jelly bean as well as the Project Build Target under Android properties of the project, didn't modify one single line of code, just that, and for some reason it does the trick...
Hope this helps.
Regards!
I had some similar issue where the inputStream.read() returned -1 and I did not get any Exception. In fact the server was down and the connection broken. I didn't test it with different versions, only with 4.0.
Here's the Google Bug Report about this behavior.
Unfortunately status of the bug seems to be 'closed' as not reproduceable.
My work around was to interpret the -1 as a close of the socket and an unreachable server. When you try to reconnect, you get the right errors.
I have had a similar problem and fixed it with a workaround like this
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private static class WatchDog implements Runnable{
private Thread thread = Thread.currentThread();
public void run() {
Log.d(LOG_TAG, "Interrupting read due to timeout");
thread.interrupt();
}
}
private void read(InputStream in, ByteBuffer bb, long waitTime) throws IOException {
int startingPos = bb.position();
long timeout = System.currentTimeMillis() + RESPONSE_TIMEOUT;
ScheduledFuture<?> watchdogFuture = executor.schedule(new WatchDog(), RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS);
try {
while(System.currentTimeMillis() < timeout && bb.hasRemaining()){ //workaround fixing timeout after 1ms
try{
int read = in.read(bb.array(), bb.position(), bb.remaining());
if(read > 0){
bb.position(bb.position()+read);
}
} catch(SocketTimeoutException e){}
if(bb.hasRemaining()){
Thread.sleep(5);
}
}
watchdogFuture.cancel(true);
} catch (InterruptedException e) {}
if(bb.hasRemaining()){
throw new SocketTimeoutException("Unable to read requested bytes: "
+ (bb.position()-startingPos) + "/" + (bb.limit()-startingPos)
+ " after " + (System.currentTimeMillis() - timeout + RESPONSE_TIMEOUT) + "ms");
}
}
Using BufferedReader and PrintWriter works on all versions for me and is extremely convenient for sending and receiving anything you want (even JSON strings) via any communication protocol. Try saving them as member variables when starting your background thread like this:
mInput = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
mOutput = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
For asynchronous communication your background thread might then look like this:
#Override
public final void run() {
while (!Thread.currentThread().isInterrupted()) {
if (mInput == null) {
break;
}
String message = null;
try {
message = mInput.readLine();
} catch (IOException e) {
// handle the exception as you like
break;
}
if (Thread.currentThread().isInterrupted()) {
// thread was interrupted while reading
break;
} else if (message != null) {
// handle the message as you like
}
}
}
Use another background thread to send messages:
#Override
public void run() {
if (mOutput != null) {
mOutput.println(<message to be );
if (mOutput == null) {
// the above thread was interrupted while writing
} else if (!mOutput.checkError()) {
// everything went fine
} else {
// handle the exception
}
}
}
Also, you will have to close the streams from outside to make sure readLine doesn't block forever:
try {
mOutput.close();
mInput.close();
mOutput = null;
mInput = null;
} catch (IOException e) {
// log the exception
}
Now, since you're using TCP sockets it may happen that the socket is actually dead and readLine is still blocking. You have to detect that and close the streams just like above. For that, you will have to add another thread (oh well) that periodically sends keep-alive-messages. If no message was received from the remote device for X seconds, it has to close the streams.
This whole approach makes sure the socket is closed and all threads finish at all circumstances. Of course you can make the communication synchronous, if that is what you need, by removing the sender-thread and including println() inside the reader-thread instead. I hope that helps you (even though the answer comes 8 months late).
Friend,
try inputStream.readLine(); (i.e) DataInputStream.readLine(); (Deprecated method)
this worked for me...
Try this code -
Runnable runnable = new Runnable() {
#Override
public void run() {
synchronized (this) {
Socket s = null;
String inMsg = null, msg2 = null;
try {
try {
s = new Socket(server, port);
} catch (Exception e) {
return;
}
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
try {
inMsg = in.readLine()
+ System.getProperty("line.separator");
} catch (Exception e) {
return;
}
out.write(message + "\n\r");
out.flush();
try {
msg2 = in.readLine();
if (msg2 == null) {
return;
}
} catch (Exception e) {
return;
}
out.close();
s.close();
} catch (Exception e) {
return;
}
}
}
};
It works for me.
You should use Apache Commons IO: http://commons.apache.org/proper/commons-io/
See IOUtils.copy() http://commons.apache.org/proper/commons-io/javadocs/api-release/index.html?org/apache/commons/io/package-summary.html

Categories

Resources