How to end a thread handling socket connection? - java

I have a thread handling a socket connection:
BufferedReader socketInput = new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
while (true)
{
String line = socketInput.readLine();
// do stuff
}
As I've read in a few answers on this site, the recommended solution is to use a flag which one thread sets and my (socket handling) thread checks and terminates itself when that flag changes state. Something like:
while (!done)
{
String line = socketInput.readLine();
// do stuff
}
But this can get stuck when readLine() is still waiting for input. I guess I could set a timeout:
mySocket.setSoTimeout(100);
while (!done)
{
String line = socketInput.readLine();
// do stuff
}
Which would probably work but I would still get a 100 ms delay before my thread "realizes" the flag's state changed.
Is there a way for the thread to "realize" right away that it should end? If not, is my solution (with timeout and flag done) correct?
Edit: I've clarified that the socketInput is of type BufferedReader (alternatively I'm considering Scanner).

The most common way to handle this is to close the socket from the other Thread. This will lead the reading side to unblock and exit with the (expected) error that the socket was closed. Depending on the socket API that you have available it might also be possible to shutdown only the reading side. From a short look at the JDK shutdownInput() might work.
If you however want to continue to read from the socket later on these obvisouly won't work. Your solution should work there, but is obvisouly worse for performance and reactivity since you basically poll the socket all 100ms.

Create a Selector
Configure your socket.getChannel() to non-blocking and register it to the Selector with SelectionKey.OP_READ
Call your Selector select() method that will return when there are some data to read so you can call readLine() (i.e. select() returns > 0)
Whenever you want to end your socket processing, set your done flag and call your Selector wakeup() method. That will make the select() return immediately (potentially 0, or 1 if there was activity). You can then check your done flag and end your thread gracefully.
Here is a quick implementation. Notice I pass the BufferedReader as an argument as if you're opening it in the thread you should also close it there, which would close the socket too, so it has to be done outside. There are two methods to signal the thread to gracefully stop processing input and one to send data:
public class SocketHandler extends Thread {
private Socket sok;
private BufferedReader socketInput;
private Selector sel;
private SocketChannel chan;
private boolean done;
public SocketHandler(Socket sok, BufferedReader socketInput) throws IOException {
this.sok = sok;
chan = sok.getChannel();
chan.configureBlocking(false);
sel = Selector.open();
chan.register(sel, SelectionKey.OP_READ);
this.socketInput = socketInput;
done = false;
}
#Override
public void run() {
while (!done) {
try {
if (sel.select() == 0)
continue;
} catch (IOException e) {
e.printStackTrace();
}
// Only one channel is registered on only one operation so we know exactly what happened.
sel.selectedKeys().clear();
doRead();
// Otherwise: loop through sel.selectedKeys(), check for readability and clear the set
}
try {
sel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void doRead() {
try {
String line = socketInput.readLine();
// TODO: process 'line'
} catch (IOException e) {
e.printStackTrace();
}
}
public void signalStop() {
done = true;
if (sel != null)
sel.wakeup(); // Get out of sel.select()
}
public void doWrite(byte[] buffer) throws IOException { // Or "String message"
sok.getOutputStream().write(buffer); // Or anything else
}
}

The solution is correct, it will exit when done is set to true.
And yes, the readLine will always wait for 100ms, if you don't want to wait you may interrupt the thread by calling thread.interrupt() it but it's not very clean way.

The best way to know when finish a socket connection is to try to read something. If read method return -1 you can end threadling socket connection
byte[] data = new byte[2048];
while (!done) {
int count = input.read(data);
if (count <= 0) {
if (count < 0)
done = true;
continue;
}
String request = new String(data, 0, count);
//do stuff
}
We try to read something in input if count == -1, the socket client is disconnected now we can end the loop, by changing the value of done.

Related

Synchronizing data output stream

I'm having a problem where I have a class that gets instantiated upon a connection to server.
The method I'm having trouble with in the class looks like so:
public void sendData(byte[] dataToSend) throws IOException {
sendLock.lock();
int dataLength = dataToSend.length;
dout.writeInt(dataLength);
dout.write(dataToSend, 0, dataLength);
dout.flush();
sendLock.unlock();
}
Where sendLock is a ReentrantLock and dout = new DataOutputStream(socket.getOutputStream());. This will work fine with a limited number of threads, but if I have a large number of threads calling this method concurrently I get a deadlock and the program just stops.
Is there any reason a deadlock would be happening here? It doesn't make sense to me, as I've removed all other locks to rule them out and I'm down to this one. Is there anyway the flush could cause things to hang or something? It just seems like at some point it never releases the lock and I'm not sure why.
If I remove the lock I get socket errors because one thread may change the dataLength before another has a chance to write, etc. But the deadlock no longer occurs.
As a reference, here's what the run method of the Receive end looks like:
public void run() {
while (socket != null) {
try {
int dataLength = din.readInt();
byte[] data = new byte[dataLength];
din.readFully(data, 0, dataLength);
Event e = ef.getEvent(data);
node.onEvent(e);
} catch (SocketException se) {
System.out.println(se.getMessage());
break;
} catch (IOException ioe) {
System.out.println(ioe.getMessage()) ;
break;
}
}
}
It's possible that one of your calls to the output stream throws an exception and sendLock.unlock() is never called. All the other threads will be left waiting forever.
Check your logs to see if one of the threads throws an exception. In your code I would use a try-catch-finally block instead of throwing IOException. This guarantees, even if something bad happens, the lock will be released so other threads can keep working.
public void sendData(byte[] dataToSend) throws IOException {
try {
sendLock.lock();
int dataLength = dataToSend.length;
dout.writeInt(dataLength);
dout.write(dataToSend, 0, dataLength);
dout.flush();
}
finally {
sendLock.unlock();
}
}

Can't detect disconnect without extra readLine() loop

I am developing a program that uses sockets and currently I have a function in my code that checks for a heartbeat from the client every second.
private void userLoop() { // checks for incoming data from client
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
try {
socketIn.read(); // check for heartbeat from client
String userInput;
while ((userInput = br.readLine()) != null) {
}
} catch (Exception e) {
ControlPanel.model.removeElement(getUsername());
ControlPanel.append(getUsername() + " has disconnected.");
}
}
}, 1000);
}
When a client closes the game via the X button, shutting off their computer, logging out, whatever it may be, I get the message "'username' has disconnected". This is exactly what I want, however, it only works with the while loop in the code. The while loop essentially does nothing and I have no idea why it doesn't work with out.
If I remove the while loop and I disconnect using my client nothing gets printed out server sided.
String userInput;
while ((userInput = br.readLine()) != null) {
}
The above is essentially the dead code that does nothing but without it my program doesn't work the way it should..
Why is the code needed and how can I remove it and still make my program work correctly?
In this case, your while loop is essentially stalling your program until you no longer receive an input string. It's not dead code; it is just your way of installing a wait.
Otherwise, based on my understanding in the Timer class, it only waits one second, which might be too short of a timespan for what you're waiting to capture.
I fixed my problem by changing everything in the try block with
br.readLine();
There's a saying I've heard about exception handling: "Exceptions should only be used for exceptional situations." A client disconnecting from a server is not exceptional.
Now that I have that off my chest, let's move on. According to this other question,
socket.getInputSteam.read() does not throw when I close the socket from the client
it sounds like the read call won't throw if you're closing things properly on the client side.
The problem is that when the remote socket is closed, read() does not throw an Exception, it just returns -1 to signal the end of the stream.
The following should work without needing to call readLine():
try {
int ret = socketIn.read(); // check for heartbeat from client
if (ret == -1) {
// Remote side closed gracefully
clientDisconnected();
}
} catch (SocketTimeoutException e) {
// Timeout -- handle as required
handleTimeout();
} catch (IOException e) {
// Connection lost due to I/O error
clientDisconnected()
}

Unblocked read from InputStream of a BlueToothSocket while checking connection of the socket

I am trying to write a thread which will do following stuffs:
1. read from inputstream;
2. some other routine;
3. if socket is closed, throw an exception.
The BlueTooth Socket and inputStream from the socket has been set up before this. The code is as following:
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
//if(mInputStream.available() > 0){ //-------- Line 1
bytes = mInputStream.read(buffer);
//} //-------- Line 2
//---------------------//
// some other routines //
//---------------------//
} catch (IOException e) {
connectionLost();
break;
}
}
}
The problem is that the above code will hang at mInputStream.read() because of the blocking. However, if I uncomment Line 1 and Line 2, the mInputStream.available() function will not throw exception even if BlueToothSocket is closed. Is there any method either to unblock read function, or to throw an exception when available() is used and BlueTooth Socket is closed? Appreciate it!
This is what I use:
private boolean receivedInTimelyManner(InputStream mInStream,
int bytesToReceive, long timeoutMillis) throws IOException,
InterruptedException {
long time = 0;
while (mInStream.available() < bytesToReceive && time < timeoutMillis) {
time+=5;
Thread.sleep(5);
}
if (time == timeoutMillis) {
return false;
}
return true;
}
Surround your read block with something like:
if receivedInTimelyManner(instream,bytes,timeout){
read()
}
Ok, seems there is not an easy way to do unblocked read() and available() throwing is not working. The most convenient way to work this out is to create another thread to do the other routines. While leave this thread alone particularly for reading inputstream and checking inputstream status(exception thrown).

Socket closing not throwing IOException

I'm writing a basic Server-client program in Java and I'm trying to handle the case where the client terminates unexpectedly.
public void run() {
while(alive) {
try {
// socketIn is a BufferedReader wrapped around the socket's InputStream.
String input = socketIn.readLine();
if(input == null)
continue;
String response = processInput(input);
// socketOut is a PrintWriter wrapped around the socket's OutputStream.
if(response != null) {
socketOut.println(response);
socketOut.flush();
}
} catch(IOException e) {
System.out.println("TRACE 1");
alive = false;
}
}
System.out.println("TRACE 2");
}
But when I kill the client, the loop keeps going and neither TRACE is printed out. I'm assuming that when a socket is closed from the other end and I am trying to read from it, it will throw an IOException.
Was this a bad assumption? What can I do to fix this?
readLine() will return null at end of stream, which is what happens when the remote end closes the connection normally. You are attempting to continue on this condition, which will loop forever. IOException will be thrown if the connection is broken abnormally.

How to unblock InputStream.read() on Android?

I have a thread in which the read() method of an InputStream is called in a loop. When there are no more bytes to read, the stream will block until new data arrives.
If I call close() on the InputStream from a different thread, the stream gets closed, but the blocked read() call still remains blocked. I would assume that the read() method should now return with a value of -1 to indicate the end of the stream, but it does not. Instead, it stays blocked for several more minutes until a tcp timeout occurs.
How do I unblock the close() call?
Edit:
Apparently, the regular JRE will throw a SocketException immediately when the stream or socket the blocking read() call corresponds to is close()'d. The Android Java runtime which I am using, however, will not.
Any hints on a solution for the Android environment would be greatly appreciated.
Only call read() when there is data available.
Do something like that:
while( flagBlock )
{
if( stream.available() > 0 )
{
stream.read( byteArray );
}
}
set the flagBlock to stop the reading.
See Java Concurrency In Practice for a really good system to cancel a thread when working with sockets. It uses a special executor (CancellingExecutor) and a special Callable (SocketUsingTask).
When the other end closes the connection your stream will return -1 on a read(). If you cannot trigger the other end to close the connection e.g. by closing your output stream, you can close the socket which will cause an IOException in the blocking read() thread.
Can you provide a short example which reproduces your problem?
ServerSocket ss = new ServerSocket(0);
final Socket client = new Socket("localhost", ss.getLocalPort());
Socket server = ss.accept();
Thread t = new Thread(new Runnable() {
public void run() {
int ch;
try {
while ((ch = client.getInputStream().read()) != -1)
System.out.println(ch);
} catch (SocketException se) {
System.out.println(se);
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
server.getOutputStream().write("hi\n".getBytes());
Thread.sleep(100);
client.close();
t.join();
server.close();
ss.close();
prints
104
105
10
java.net.SocketException: Socket closed
We were having the same issue: no exception when switching network (e.g. switching from 3G to WiFi while downloading).
We are using the code from http://www.androidsnippets.com/download-an-http-file-to-sdcard-with-progress-notification, which is working perfectly except in some cases when the network connection was lost.
The solution was specifying a timeout value, this is set standard to 0 (meaning: wait infinitely).
HttpURLConnection c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.setReadTimeout(1000);
c.connect();
Experiment with a timeout value appropriate for you.
I had such issue on Samsung 2.3. When switching from 3G to Wifi InputStream.read() method blocks. I tried all tips from this topic. Nothing helped. From my prospective this is device specific issue because it should throw IOException due to javadoc. My solution is to listen for android broadcast android.net.conn.CONNECTIVITY_CHANGE and close connection from another thread it will cause IOException in blocked thread.
Here is code example:
DownloadThread.java
private volatile boolean canceled;
private volatile InputStream in;
private boolean downloadFile(final File file, final URL url, long totalSize) {
OutputStream out = null;
try {
Log.v(Common.TAG, "DownloadThread: downloading to " + file);
in = (InputStream) url.getContent();
out = new FileOutputStream(file);
return copy(out, totalSize);
} catch (Exception e) {
Log.e(Common.TAG, "DownloadThread: Exception while downloading. Returning false ", e);
return false;
} finally {
closeStream(in);
closeStream(out);
}
}
public void cancelDownloading() {
Log.e(Common.TAG, "DownloadThread: cancelDownloading ");
canceled = true;
closeStream(in); //on my device this is the only way to unblock thread
}
private boolean copy(final OutputStream out, long totalSize) throws IOException {
final int BUFFER_LENGTH = 1024;
final byte[] buffer = new byte[BUFFER_LENGTH];
long totalRead = 0;
int progress = 0;
int read;
while (!canceled && (read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
totalRead += read;
}
return !canceled;
}
You could use java.nio package. NIO stands for Non-blocking IO. Here the calls (to say read & write) aren't blocked. This way you can close the stream.
There is a sample program you can look at here. Method: processRead

Categories