Can't close InputStreamReader - android

I have some weird problem on Android 2.2 phone.
I open the socket connection:
socket = new Socket(serverAddr, SERVERPORT);
messageWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), Charset.forName("UTF-8"))));
messageReader = new InputStreamReader(socket.getInputStream(), Charset.forName("UTF-8"));
Start reading in AsyncTask doInBackground():
protected Boolean doInBackground(final Void... unused) {
...
char[] chars = new char[256];
int len;
while((len = messageReader.read(chars))>=0) {
builder.append(chars, 0, len);
break;
}
result = true;
...
}
After reading I close the connection:
try {
socket.close();
socket = null;
} catch (Exception e) {Log.e("socket", "ex");}
try {
messageWriter.close();
messageWriter = null;
} catch (Exception e) {Log.e("writer", "ex");}
try {
messageReader.close();
messageReader = null;
} catch (Exception e) {Log.e("reader", "ex");}
Everything is working fine on Android 2.3 phone and Android 4.0 emulator, but for some reason it's not working on Android 2.2 phone.
The socket is closed, the writer is closed, but the reader is stuck in a while loop even though it should brake loop when calling "socket.close();"... so my closing code is stuck on "messageReader.close();" and whole UI is blocked! ...and there is no way to kill AsyncTask... Why is this happening and what should I do?

First, you don't need those three closes. You only need to close the output stream/writer.
Second, closing isn't guaranteed to terminate a blocking operation in another thread. See the Javadoc. If you have such a thing you need to shutdown the socket for input before closing. Then the reading thread will get an EOS, on which it should exit.

Related

Sending bluetooth data between Android and Hololens

I have created an application to Android and Microsoft Hololens, where it is possible to send some GPS-data with bluetooth from an Android-phone to a Hololens (with Bluetooth LE Advertiser) and that works allright. But when I am trying to send other data from Hololens to Android, I have a problem that Android-phone can't discover Hololens, although these devices are paired. Is it even possible to send data from Hololens with bluetooth, or is there only something wrong in my code? Does Bluetooth LE Advertising support two-way data transfering?
I am guessing you have a BluetoothConnected thread in your android app with an InputStream (mine is mmInStream). Try using this as your 'run' function in the thread:
public void run() {
System.out.println("BT THREAD RUNNING");
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
InputStreamReader mmInStreamReader = new InputStreamReader(mmInStream);
BufferedReader mmReader = new BufferedReader(mmInStreamReader);
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
Thread.sleep(100);
String s = mmReader.readLine();
Thread.sleep(100);
//Static class that handles the response
BluetoothCommunications.responseHandler(s);
} catch (IOException e) {
System.out.println("Input stream was disconnected" + e);
main.disconnected();
break;
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}

Android TCP Socket InputStrem Intermittent Read or too Slow

I need to implement a TCP comunication between an IoT device(custom) and an Android App.
For the Wifi device we have a Server Socket, while in Android i have an AsyncTask as a Client Socket. Both the device and the smarthone are connected to the same network.
Here is the Android Client Socket code for the initialization/socket-read and socket-write:
Variables:
static public Socket nsocket; //Network Socket
static public DataInputStream nis; //Network Input Stream
static private OutputStream nos; //Network Output Stream
AsyncTask method doInBackgroud:
#Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
//Init/Create Socket
SocketInit(IP, PORT);
// Socket Manager
SocketUpdate();
} catch (IOException e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: IOException");
clearCmdInStack();
MainActivity.SocketDisconnectAndNetworkTaskRestart();
result = true;
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
result = true;
} finally {
try {
SocketDisconnect();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.i("AsyncTask", "doInBackground: Finished");
}
return result;
}
Socket Initializzation:
public void SocketInit(String ip, int port) throws IOException {
InetAddress addr = InetAddress.getByName(ip);
SocketAddress sockaddr = new InetSocketAddress(addr, port);
nsocket = new Socket();
nsocket.setReuseAddress(false);
nsocket.setTcpNoDelay(true);
nsocket.setKeepAlive(true);
nsocket.setSoTimeout(0);
nsocket.connect(sockaddr, 0);
StartInputStream();
StartOutputStream();
}
Read from Socket:
private void SocketUpdate() throws IOException, ClassNotFoundException {
int read = 0;
// If connected Start read
if (socketSingleton.isSocketConnected()) {
// Print "Connected!" to UI
setPublishType(Publish.CONNECTED);
publishProgress();
if(mConnectingProgressDialog != null)
mConnectingProgressDialog.dismiss(); //End Connecting Progress Dialog Bar
//Set Communications Up
setCommunicationsUp(true);
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
byte[] buffer = new byte[3];
do{
nis.readFully(buffer, 0, 3);
setPublishType(Publish.READ);
publishProgress(buffer);
}while(!isCancelled());
SocketDisconnect();
}
}
Streams init:
public void StartInputStream() throws IOException{
nis = new DataInputStream(nsocket.getInputStream());
}
public void StartOutputStream() throws IOException{
nos = nsocket.getOutputStream();
}
Read and Write methods:
public int Read(byte[] b, int off, int len) throws IOException{
return nis.read(b, off, len); //This is blocking
}
public void Write(byte b[]) throws IOException {
nos.write(b);
nos.flush();
}
public boolean sendDataToNetwork(final String cmd)
{
if (isSocketConnected())
{
Log.i("AsyncTask", "SendDataToNetwork: Writing message to socket");
new Thread(new Runnable()
{
public void run()
{
try
{
Write(cmd.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
Log.i("AsyncTask", "SendDataToNetwork: Message send failed. Caught an exception");
}
}
}).start();
return true;
}
Log.i("AsyncTask", "SendDataToNetwork: Cannot send message. Socket is closed");
return false;
}
The application is very simple, the android app sends a command(via sendDataToNetwork method) to the IoT device and the latter sends back an "ACK" Command string.
The problem
The problem is that while the IoT device always receives the command, the smartphone rarely gets the ACK back. Sometimes i get something like "ACKACKACKACK". By debugging the IoT device i'm sure that it successfully sends back the ACK, so the problem lies in the InputStream read() method which doesn't retrieve the string right away.
Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?
Update
I've updated the socket config so that there are no more buffer limitations and i've replaced read() method with readFully. It greatly improved, but still make some mistakes. For istance one out of 2-3 times no ack is received and i get 2 ack the next turn. Is this perhaps the computational limit of the IoT device? Or is there still margin for a better approach?
the problem lies in the InputStream read() method which doesn't empty the buffer right away.
I don't know what 'empty the buffer' means here, but InputStream.read() is specified to return as soon as even one byte has been transferred.
Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?
The actual problem is that you could be reading more than one ACK at a time. And there are others.
If you're trying to read exactly three bytes, you should be using DataInputStream.readFully() with a byte array of three bytes.
This will also get rid of the need for the following array copy.
You should not mess with the socket buffer sizes except to increase them. 20 and 700 are both ridiculously small values, and will not be the actual values used, as the platform can adjust the value supplied. Your claim that this improved things isn't credible.
You should not spin-loop while available() is zero. This is literally a waste of time. Your comment says you are blocked in the following read call. You aren't, although you should be. You are spinning here. Remove this.

Android Udp : How to receive datagramPacket from LAN

I have an UDP server wrote in C langage which broadcasts paquets over my LAN every 5seconds, on port 3001.
i'm creating an android application as UDP client, which is listening on port 3001 (in the AsyncTask thread) and it's running until the receive() method, no data seems to be detected on this port.
Here is my code :
private class ConnectionTask extends AsyncTask<Void, Integer, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... arg0) {
String receivedString = "";
byte[] receiveData = new byte[1024];
DatagramSocket clientSocket;
try {
while(true){
clientSocket = new DatagramSocket(5000);
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
clientSocket.receive(receivePacket);
receivedString = new String(receivePacket.getData());
}
} catch (SocketException e) {
Log.v("SocketExceptionOccured", e.toString());
e.printStackTrace();
//clientSocket.close();
} catch (IOException e) {
Log.v("IOExceptionOccured", e.toString());
e.printStackTrace();
//clientSocket.close();
}
Toast.makeText(getBaseContext(), receivedString, Toast.LENGTH_LONG)
.show();
return null;
}
}
I test my code with my own device for debug, with USB cable.
I've tested my server with a simple UDP client (in C) running on my computer, and the communication is ok.
I don't know why this code doesn't work. Has someone an idea ?
Thanks,
You're never leaving the while loop. You're message is probably received, and after it, the loop causes the datagramsocket to listen again.
Don't create and close the socket every time around the loop. Create it first and close it afterwards. At present there are windows of time during which the socket doesn't exist, so datagrams to it are dropped: also, all queued datagrams are dropped every time you close it.
I had this same problem. You need to add permissions in the android manifest
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
Also enable broadcasts in the socket.
clientSocket.setBroadcast(true);
Like everyone else before me mentioned, your code also never leaves the while loop, so it never goes to the the line where the toast is displayed.Remember that you CANNOT show Toast messages from doInBackground as this is accessing the UI Thread, you can only do so from the postExecute and preExecute functions. This will cause your application to crash. To check the data you receive you can either debug it or log it.
Your final doInBackground should be something like this
#Override
protected Void doInBackground(Void... arg0) {
String receivedString = "";
byte[] receiveData = new byte[1024];
DatagramSocket clientSocket;
try {
while(true){
clientSocket = new DatagramSocket(5000);
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
clientSocket.setBroadcast(true);
clientSocket.receive(receivePacket);
receivedString = new String(receivePacket.getData());
Log.i("Received String= "+receivedString);
}
} catch (SocketException e) {
Log.v("SocketExceptionOccured", e.toString());
e.printStackTrace();
//clientSocket.close();
} catch (IOException e) {
Log.v("IOExceptionOccured", e.toString());
e.printStackTrace();
//clientSocket.close();
} finally{
if(clientSocket!=null){
clientSocket.close();
}
}
return null;
}
Now when you check your logs, you should be able to see the value of the string received.

Android Serversocket does not seem to accept connections on emulators

I've been trying to implement a simple socket communication between two Android emulators but just can't seem to get it.
My server:
public void run() {
if (SERVERIP != null) {
try {
serverStatus.setText("My IP: " + SERVERIP);
serverSocket = new ServerSocket(6798);
serverStatus.setText("ServerSocket Created");
}
catch(Exception e) {
e.printStackTrace();
}
try {
while (true) {
serverStatus.setText("waiting for client");
Socket client = serverSocket.accept();
serverStatus.setText("Connected.");
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String line = in.readLine();
serverStatus.setText(line);
in.close();
client.close();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
else
serverStatus.setText("Couldn't detect internet connection.");
}
My Client:
try {
InetAddress ina = InetAddress.getByName("10.0.2.2");
socket = new Socket(ina, 6789);
}
catch (Exception e) {
e.printStackTrace();
}
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
out.println("Hey Server!");
dispText.setText("sent");
}
catch (Exception e) {
e.printStackTrace();
}
The Client side goes on to display the message "sent" however the Server does not move beyond "waiting for client" (stuck on the blocking accept()).
I've used telnet on my Windows machine to redirect port 6789 to 6798 on the server emulator's console. I've also tried turning off my firewall and the other suggestions posted on the similar questions asked here. Please help as just can't seem to get it and feel like I'm making a very stupid mistake.
Also, can anyone please tell me how it is possible for the Client to move beyond the Socket creation code line if the Server is still stuck on accept(). Or, does it not matter to the client that the Server isn't responding as long as it is listening on the port??
Android emulators are placed behind a virtual firewall/router by design, and cannot see each other, even when they are on the same network. The "Using Network Redirection", as well as "Interconnecting Emulator Instances" part of Google's doc on the emulator explains how to communicate with an emulator instance.
As for your last question. Use the empty constructor for socket, and then use the connect call with a specified timeout.

TCPsending crashes the App if server is down

i wrote a Server for our global Leadbord which actually works now.
I can send data to it if it's active. But if it's down my app does not stop. I dont get a solution for it i hope you can help me. Here is the sending:
public void saveToDatabase(LeadboardElement element2) {
final LeadboardElement element = element2;
send = false;
// Need to be a thread! else android blocks it because it could take to
// long to send!
this.thread = new Thread() {
public void run() {
try {
Socket soc = new Socket(Config.TCP_SERVERNAME_IP,
Config.TCP_PORT);
DataOutputStream out = new DataOutputStream(
soc.getOutputStream());
DataInputStream in = new DataInputStream(
new BufferedInputStream(soc.getInputStream()));
// to call the save statement!
out.writeInt(0);
// give the stuff
out.writeUTF(element.getName());
out.writeInt(element.getLevel());
out.writeInt(element.getKillPoints());
// close it
out.close();
in.close();
soc.close();
send = true;
//join at every error
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
};
// start it
thread.start();
// join thread
if (!send) {
boolean retry = true;
while(retry)
try {
this.thread.join();
retry = false;
Log.w(TAG, "sending to server stopped!");
} catch (InterruptedException e2) {
Log.w(TAG, "Thread could not be joined");
}
}
}
I noticed that i need to do it in a thread since API 5 so it works like this. It's called at the end of an Game if the player touches the screen. Everything get stopped and the data is sent to the Server. If hes down it does not work we stuck in the fade to black.
I guess i need something like a timeout. I tried it with a CountDownTimer but this acutally does not solve the problem.
Thanks alot!
Changing the way you initialize the socket, you can set a timeout.
Socket s1 = new Socket();
s1.setSoTimeout(200);
s1.connect(new InetSocketAddress("192.168.1." + i, 1254), 200);
Add a timeout when creating a new Socket

Categories

Resources