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.
Related
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.
I'm writing code to simulate the structure of an in memory distributed database. I have a central server class and a slave server class. The central server instance communicates with all of the slave instances to get information from them. The method makeSlaveRequest() is part of the central server class and is shown below:
private ArrayList<String> makeSlaveRequest(SlaveServer slave, String artist) {
int port = slave.getPort();
ArrayList<String> result = new ArrayList<String>();
try(Socket slaveConnection = new Socket("localhost", port)) {
System.out.println("Entered makeSlaveRequest");
PrintWriter outToSlave = new PrintWriter(slaveConnection.getOutputStream(), true);
BufferedReader inFromSlave = new BufferedReader(new InputStreamReader(slaveConnection.getInputStream()));
outToSlave.println(artist);
String line = inFromSlave.readLine();
while(line != null) { // I'm pretty sure that we're getting stuck here
if(line.equals("No songs by that artist")) {
break;
}
System.out.println("Reading from slave: " + line);
result.add(line);
System.out.println(result);
line = inFromSlave.readLine();
}
System.out.println("Slave connection closed");
slaveConnection.close();
} catch(UnknownHostException uhe) {
System.out.println("unkown host");
uhe.printStackTrace();
} catch(IOException ioe) {
System.out.println("IOException: " + ioe);
ioe.printStackTrace();
}
System.out.println(result);
return result;
}
All calls to System.out.println() are purely for debugging purposes.
The point the code gets stuck at is the while loop. Once the last relevant song has been read in from the slave server, I'm expecting the while loop to terminate and "Slave connection closed" to be printed, however the while loop never terminates. I think this must be due to the fact that line never equals null, however, I would expect line to equal null once the last song has been sent from the slave and read in by readLine(). Here is the code for the SlaveServer's handleConnection() method, which is the method communicating with makeSlaveRequest():
protected void handleConnection(Socket connection) throws IOException {
BufferedReader inFromCentral = new BufferedReader(new InputStreamReader(connection.getInputStream()));
PrintWriter outToCentral = new PrintWriter(connection.getOutputStream(), true);
System.out.println("Entered the slave server handle connection method");
String reqArtist = inFromCentral.readLine();
System.out.println(reqArtist);
if(songDir.containsValue(reqArtist.trim())) {
System.out.println("Contains reqArtist");
List<String> songList = new ArrayList<String>();
System.out.println("songList created");
for(Map.Entry<String, String> entry : songDir.entrySet()) {
System.out.println("entered for loop");
if(entry.getValue().equals(reqArtist)) {
songList.add(entry.getKey());
System.out.println("entry added");
}
}
for(String song : songList) {
System.out.println("slave output: " + song);
outToCentral.println(song);
}
} else {
System.out.println("No Req Artist");
outToCentral.println("No songs by that artist");
}
System.out.println("flushing");
outToCentral.flush();
System.out.println("EOM");
}
}
It should be noted that each SlaveServer instance is running in it's own thread, and whenever a connection is made to SlaveServer, it uses a thread from a thread pool to handle it. CentralServer is the same.
Why is the while loop not terminating?
readLine() will only return null when it reaches EOF, which for a socket connection will be when remote side closes. If the slaves don't close connection and stop, you will be looping forever (well, readLine() will block). So, you need to decide on a method that indicates that the slave is done (if you don't want it to close socket when it's done).
The slave never closes the connection/stream, so the central server has no way to know that there is no next line after the last line it received, so readLine() blocks, waiting for the next line to be sent by the slave, or for the connection to be closed and the end of stream thus being reached.
Basically the server side sends a keep alive message every 8 minutes, if the write fails it disconnects the client and closes the socket connection. If my android device is awake and the server closes the connection then the read operation on the android device throws an exception as it should and i disconnect from the server. If the device is asleep it doesn't read data at all even with a partial wake lock and a wifilock, i have already given up on that, but my actual problem is when my device comes back from sleep (if i turn the screen on for example) what i do is send a message to the server so i can refresh the data but if my server has already closed the socket my write operation should throw an IOException but for some reason it doesn't. And even the blocking read i have doesn't throw any exception or return -1.
here is my write operation:
public boolean sendData(byte[] data)
{
boolean sent=false;
if(connectedToServer)
{
try
{
myOutputStream.write(data, 0, data.length);
sent= true;
}
catch (IOException e)
{
e.printStackTrace();
unexpectedDisconnectionFromServer();
}
}
return sent;
}
and here is my read operation:
public void startReadingInBackground()
{
while(connectedToServer)
{
try
{
int bytesRead=0;
if(myWifiLock!=null && !myWifiLock.isHeld())
myWifiLock.acquire();
byte val=(byte)myInputStream.read();
myWakeLock.acquire();
if(val==-1)
{
unexpectedDisconnectionFromServer();
if(myWifiLock!=null && myWifiLock.isHeld())
myWifiLock.release();
myWakeLock.release();
return;
}
bytesRead=myInputStream.read(myBuffer, 0, bufferSize);
if(bytesRead<1)
{
unexpectedDisconnectionFromServer();
if(myWifiLock!=null && myWifiLock.isHeld())
myWifiLock.release();
myWakeLock.release();
return;
}
byte[] dataArray=Arrays.copyOfRange(myBuffer,0,bytesRead);
ByteBuffer data=ByteBuffer.allocate(bytesRead+1).put(val).put(dataArray);
myParent.invokeReceiveAction(data, bytesRead + 1);
}
catch (IOException e)
{
if(!myWakeLock.isHeld())
myWakeLock.acquire();
unexpectedDisconnectionFromServer();
e.printStackTrace();
}
finally
{
if(myWifiLock!=null && myWifiLock.isHeld())
myWifiLock.release();
if(myWakeLock!=null && myWakeLock.isHeld())
myWakeLock.release();
}
}
}
and i get the outputstream like so:
Socket mySocket = new Socket(SERVER_IP, SERVER_PORT_TCP );
myOutputStream=mySocket.getOutputStream();
Your write will throw an IOException, eventually. Your mistake is in assuming it is bound to happen on the first write after the disconnect. It won't, for all sorts of reasons including buffering and retries. TCP has to determine that the connection is really dead before it will reject a new write, and it certainly won't do that on the first write after the disconnect.
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).
I have a Java program that reads data from a TCP source all works fine, except when my program (which acts as a client to the data source) is faster then then the source can respond BufferedReader.ready() throws an exception that closes my TCP connection, as it should. Is there any preferred way/method that I can keep the BufferedReader waiting for new input since my source can sometimes have a slight delay.
Here is the part that i am talking about:
public aDataServer(String host, int port, StreamConnection aConnection) throws UnknownHostException, IOException {
this.aConnection = aConnection;
ndataServerSocket = new Socket(Inet4Address.getByName(host),port);
ndataServerReader = new BufferedReader(new InputStreamReader(ndataServerSocket.getInputStream()));
}
public void run() {
try {
RemoteDevice dev = RemoteDevice.getRemoteDevice(aConnection);
OutputStream outputStream = aConnection.openOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(outputStream);
do {
try {
String ndata = ndataServerReader.readLine();
osw.write(ndata+"\n");
osw.flush();
LOG.log(Level.INFO,"Sent");
} catch(IOException io) {
LOG.log(Level.SEVERE, "Client device({0}) disconnected: \n{1}", new Object[]{dev.getFriendlyName(true), io.getMessage()});
break;
}
}while(ndataServerReader.ready());
} catch (IOException ioe) {
LOG.severe(ioe.getMessage());
} finally {
try {
if (ndataServerSocket != null) {
ndataServerSocket.close();
}
if (ndataServerReader
!= null) {
ndataServerReader.close();
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, ex.getMessage());
}
}
You shouldn't be using ndataServerReader.ready(). Your do/while loop (which should almost certainly just be a while loop) appears to assume that ndataServerReader.ready() indicates there's more data to be read, which is not what it's for.
The Javadoc for Reader describes the ready() method as:
Tells whether this stream is ready to be read.
Returns: True if the next read() is guaranteed not to block for input, false
otherwise. Note that returning false does not guarantee that the next
read will block.
In other worse, Reader.ready() will return false if the reader will wait for more data before returning if you attempt to read from it. This does not mean the Reader is done, and in fact you should expect this method to return false often when working with a network stream, as it could easily have delays.
Your code currently is likely reading one line (in the do block) then checking if the reader is ready (in the while), which it probably isn't, and then exiting successfully. No exceptions are being thrown - you'd see a SEVERE level logging message if they were.
Instead of using ready(), take advantage of the documented behavior of readLine() that says it returns:
A String containing the contents of the line, not including any
line-termination characters, or null if the end of the stream has been
reached
In other words, simply doing:
String ndata = reader.readLine();
while (ndata != null) {
osw.write(ndata+"\n");
osw.flush();
LOG.log(Level.INFO,"Sent");
ndata reader.readLine();
}
Is sufficient to read the whole input stream.
Reference reading: What's the difference between (reader.ready()) and using a for loop to read through a file?