Java the best way of waiting & getting data from your client - java

I started learning networking with the main networking package in JDK, it's pretty simple and easy after a few examples. But now I am interested into making multi-client applications like a chat system.
My structure idea so far is like this:
Connection handler class, which handles incoming connections, and holds the list of clients.
If new connection was found, create a new client object, start it's thread (Client object will implement runnable, so it will start it's own looping service, it will loop for new packets received), and add it to the list.
I create a new thread for each client instead of looping through all clients because the reading from client process stops the whole execution and will wait for the client to send data, which is kinda annoys me and this is my issue there.
I have created a simple console app that receives messages from the client, but now I want to detect disconnections. I read that bufferedReader .read() method returns -1 if user is not connected, so I thought I could loop and do that every number of seconds to every client, but the thing is, the client must send a packet in order to .read() it, so let's say if you do .read() it will wait & stop the whole thread until packet is received, (I think).
This is my current code which gets messages from client:
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
Basically what happens here, I am sending a integer packed from the client, I might have many things to do so therefore I can set many unique packet ID's, so if I want to receive and process a chat message, the packet id is 216, the client sends a int 216, server reads the packet, enters the switch loop of all packet ids and detects if its really 216, if yes it gets the instance of the packed class that handles messages & gets the bytes of the received message like this:
public class Chat implements Packet {
#Override
public void processPacket(Session c) {
String message = readMessage(c);
System.out.println("Message: " + message);
}
private String readMessage(Session c) {
byte[] data = c.readBytes();
String message = null;
try {
message = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return message;
}
}
And this is how I read bytes:
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
Okay my problem:
after adding the is disconnected detection, when I send my message, nothing happens. This is probably due to the .read() it stops and is waiting for a response. BUT if I write a message again, I will get the message in server.
This is my temporary, ugly client:
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 43594);
Scanner r = new Scanner(System.in);
PrintWriter out = new PrintWriter(socket.getOutputStream());
String input;
while(true) {
input = r.next();
if (input != null) {
sendMessage(input, out);
}
}
}
public static void sendMessage(String message, PrintWriter out) {
byte[] encoded = encode(message);
out.write(0);
out.println(encoded + "\n");
out.flush();
}
public static byte[] encode(String s) {
return DatatypeConverter.parseBase64Binary(s);
}
public static String decode(byte[] s) {
return DatatypeConverter.printBase64Binary(s);
}
}
My question is: What is a better way of reading data from client without making the application wait for it and actually loop everytime? OR maybe should I have a new thread for checking if user is online so it's 2 threads per 1 client?
If someone needs my session object (client object):
public class Session extends Thread implements Runnable {
private Socket session;
private Client client;
private PrintWriter out;
private BufferedReader in;
private PacketHandler packets;
private DataInputStream dataIn;
private ConnectionHandler connection;
private final int checkTime = 1600;
private final int maxTime = 22000;
private long lastCheck;
public Session(Socket session) {
this.session = session;
this.client = new Client(this);
try {
this.setStream();
} catch (IOException e) {
e.printStackTrace();
}
this.packets = new PacketHandler(this);
System.out.println("[New session created]: " + session.getRemoteSocketAddress());
}
public void setConnectionHandler(ConnectionHandler c) {
this.connection = c;
}
public void run() {
try {
this.startClientService();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setStream() throws IOException {
this.out = new PrintWriter(this.session.getOutputStream());
this.in = new BufferedReader(new InputStreamReader(this.session.getInputStream()));
this.dataIn = new DataInputStream(this.session.getInputStream());
}
public Client getClient() {
return this.client;
}
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public String readMessage() {
try {
return this.in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
}
Thanks!

While I don't have time to look over all the code, here are two things that could help you out.
1) Use a defined message header. Define X number of bytes of each message that the client will send to the server. Use these bytes to define how long the message will be, and what type of message it is. The server knows the length and layout of this header, and uses it to process the message in a particular way. Example could be a header of one byte. A value of 1 could be a I'm connected message. 2 could be I'm about to disconnect. 3 could be I'm currently away, and 4 could be an incoming chat message.
2) There are 2 ways you can handle the input. First is to use blocking IO, and create a separate thread to receive messages from each client. I believe this is what you are currently doing. The second is to use non-blocking IO, and have a separate thread iterate over the open sockets and do a read. Non-blocking will check if there is data to read, but if there is not, the thread will continue executing.

Related

Java can't get a continuous reading/writing to a socket

I'm studying electronic engineering but for my internship I've been asked to make a program that must communicate via a socket to a certain ip and port. The program has a main class which is the GUI and I made another class for the connection which runs parallel using a Thread. The problem is that when I click the "Connect" button that I have on my GUI, it only receives or sends once the data, despite having a while loop. To further receive and send data I must spam-click my connect and disconnect button.
public class ConnectionSocket implements Runnable {
private final int port;
private final String ipAddr;
private final Observer observador;
private DataInputStream in;
private DataOutputStream out;
private Socket sc = null;
private boolean inputAvailable = false;
public boolean lastInput = false;
private String nextOutputMessage;
private boolean disconnect=false;
private Observable observable;
public ConnectionSocket(String ip, int p , Observer panel) {
this.ipAddr = ip;
this.port = p;
this.observador = panel;
}
#Override
public void run() {
this.observable = new Observable();
observable.addPropertyChangeListener(observador);
this.observable.notifyData("Message Connected");
System.out.print("\n**********************\nConectado\n**********************\n");
while (!disconnect) {
try {
try {
sc = new Socket(this.ipAddr , this.port);
}
catch (IOException e) {
System.out.println("Socket TimeOut");
}
if(sc != null) {
out = new DataOutputStream(sc.getOutputStream());
in = new DataInputStream(sc.getInputStream());
}
while(!disconnect){
if (sc != null) {
try {
if(in.available() != 0) {
receiveData();
}
else {
inputAvailable = false;
}
}
catch(IOException ex) {
System.out.println("***Read or write error***");
System.out.println(ex.toString());
}
}
}
if ( sc != null ) {
try { sc.close(); }
catch ( IOException e ) {}
}
} catch (IOException ex) {
Logger.getLogger(ConnectionSocket.class.getName()).log(Level.SEVERE, null, ex);
}
}
//Close the socket
cutConnection();
System.out.print("\n**********************\nDesconectado\n**********************\n");
}
public void sendNewData(String msg) throws IOException { //this method receives data from the GUI//
nextOutputMessage = msg;
sendData();
}
private synchronized void sendData() throws IOException {
System.out.println("Panel: " + nextOutputMessage);
out.writeByte((byte) Integer.parseInt(nextOutputMessage));
}
private synchronized void receiveData() throws IOException {
if(!inputAvailable)System.out.print("CUBE: ");
inputAvailable = true;
while(in.available() != 0) {
byte dat = (byte) in.read();
this.observable.notifyData(Character.toString((char) dat));
System.out.print((char) dat);
}
}
public void cutConnection() {
try {
disconnect = true;
if(this.in != null) {
this.in.close();
}
if(this.out != null) {
this.out.close();
}
if(this.sc != null) {
this.sc.close();
}
} catch (IOException ex) {
this.sc = null;
}
}
}
I use two additional classes which use PropertyChangeListener to send the data received to the GUI, otherwise it just blocks it.
I receive and send data as ASCII code.
This is the method in the GUI class which starts the thread (the connection)
private void connect(String ip, int port) {
jButton_connect.setText("Desconectar");
labelMessage("Conectando...");
observerPanel = new Observer(this);
connection = new ConnectionSocket(ip , port, observerPanel);
Thread t = new Thread(connection);
t.start();
}
So basically I would appreciate any hint about what I'm doing wrong, because I can't really find any solution suitable for my needs. BTW any solution that includes getting rid of the double while loop would be great, as it consumes a lot of cpu in that point. Feel free to criticise any stupid thing I made because I had to learn java from zero to do this so it's probably not really good.
Thanks in advance
Edit 1: Does the sc = new Socket(this.ipAddr , this.port); sentence need to be called in the loop or once it's called it permanently bounds to that ip and port? I only need to connect to a specific ip and port. Thanks

Thread for reading from socket stream takes more CPU usage

In client socket, I wrote a thread to read the socket's inputStream continuously. Here I have used a while loop to read infinitely. However it takes more CPU; hence is it possible to reduce the CPU. Please add your suggestions.
Also is it possible to add listeners for inputStream.
Thread code:
public void run() {
while (!shutdown) {
try {
if(socketClient != null) {
String message = socketClient.getMessage();
logger.info ("Message size:" + message.length ());
if(!message.equals("EmptyString")) {
process(message);
}
}
} catch (Exception exception) {
logger.info("Unable to read the socket message" +exception);
}
}
}
SocketClient.java
public class SocketClient{
private volatile boolean isConnected;
private int port;
private int retryCount;
private long startTime;
private String hostName;
private DataInputStream input;
private DataOutputStream output;
private Socket socket;
public SocketClient(int port, String hostname) throws IOException {
this.port = port;
this.hostName = hostname;
establishConnection();
}
public void shutdown() {
try {
shutdown = true;
input.close();
output.close();
socket.close();
} catch (Exception e) {
logger.debug("Exception in shutdown:" + e.getMessage());
}
}
public String getMessage() {
BufferedReader reader = null;
try {
StringBuilder builder = new StringBuilder();
reader = new BufferedReader(new
InputStreamReader(tcpSocket.getInputStream()));
do {
builder.append(reader.readLine());
} while((reader.ready()));
if (builder.length() == 0)
return "EmptyString";
return builder.toString();
} catch (IOException e) {
return "EmptyString";
} finally {
try {
if(reader != null)
reader.close();
} catch(IOException e) {
logger.error("unable to close reader");
}
}
}
private void establishConnection() {
retryCount = 1;
startTime = System.currentTimeMillis();
while (!shutdown) {
try {
if(!isConnected) {
socket = new Socket(hostName,port);
socket.setKeepAlive(true);
input = new DataInputStream(socket.getInputStream());
output = new DataOutputStream(socket.getOutputStream());
isConnected = true;
shutdown = true;
}
} catch (Exception exception) {
isConnected = false;
sleepFewSeconds();
reconnectSocket();
}
}
}
private void reconnectSocket() {
long endTime = startTime + 120000L;
if(!(System.currentTimeMillis() < endTime)) {
shutdown = true;
}
}
private void sleepFewSeconds() {
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException interruptedException) {
shutdown = true;
}
}
}
I am going to critique the entire class here. The answer to your specific question will appear.
public class SocketClient{
private volatile boolean isConnected;
You don't need this. socket == null would do just as well.
private int port;
private int retryCount;
private long startTime;
private String hostName;
private DataInputStream input;
private DataOutputStream output;
private Socket socket;
public SocketClient(int port, String hostname) throws IOException {
this.port = port;
this.hostName = hostname;
establishConnection();
}
public void shutdown() {
try {
shutdown = true;
input.close();
output.close();
socket.close();
You don't need all these closes, and you're doing them in the wrong order anyway. output.close() is sufficient and in any case it should certainly be first.
} catch (Exception e) {
logger.debug("Exception in shutdown:" + e.getMessage());
}
}
public String getMessage() {
BufferedReader reader = null;
The BufferedReader should be an instance variable, not a local variable. It's buffered. If you make it a local variable you will lose data.
try {
StringBuilder builder = new StringBuilder();
reader = new BufferedReader(new
InputStreamReader(tcpSocket.getInputStream()));
do {
builder.append(reader.readLine());
} while((reader.ready()));
You don't need all this. If the message is a single line, all you need is return reader.readLine(), and you need the caller to check whether it was null, and if so close the socket, cease reading, etc. If the message is more than one line, this is a misuse of ready(): it is certainly not an indicator of end of message. It appears from comments under your question that you shouldn't even have the method: just connect the socket input stream directly to your XML parser and let it do the reading.
if (builder.length() == 0)
return "EmptyString";
Don't do this. Return "" or null. Don't make up new magic strings for your application to have to decode.
return builder.toString();
} catch (IOException e) {
return "EmptyString";
Ditto.
} finally {
try {
if(reader != null)
reader.close();
You should not close the reader here. Closing it will close the socket, so you can never get another message.
} catch(IOException e) {
logger.error("unable to close reader");
}
}
}
private void establishConnection() {
retryCount = 1;
startTime = System.currentTimeMillis();
while (!shutdown) {
try {
if(!isConnected) {
socket = new Socket(hostName,port);
socket.setKeepAlive(true);
input = new DataInputStream(socket.getInputStream());
output = new DataOutputStream(socket.getOutputStream());
isConnected = true;
shutdown = true;
Why are you setting shutdown to true here? Nothing is shutdown yet. It's a brand new socket.
}
} catch (Exception exception) {
isConnected = false;
sleepFewSeconds();
reconnectSocket();
}
Poor practice. Socket.connect(), which is called internally by new Socket(...), already retries, and also you should distinguish between connection-failure exceptions rather than adopt the same strategy for them all. For example, a 'connection timeout' will already have blocked for a minute or so: you don't need another sleep; and 'connection refused' means there is nothing listening, so retrying is completely pointless.
private void reconnectSocket() {
long endTime = startTime + 120000L;
if(!(System.currentTimeMillis() < endTime)) {
shutdown = true;
}
}
private void sleepFewSeconds() {
try {
TimeUnit.MILLISECONDS.sleep(20);
This is not a 'few seconds'. It is 20 milliseconds, and that is not enough by at least two orders of magnite in network programming, to the extent that there should be any sleep at all of course.
} catch (InterruptedException interruptedException) {
shutdown = true;
shutdown appears to be never false. I doubt that you've thought through what it really means, and I doubt that you really need it at all.
As for your calling code:
public void run() {
while (!shutdown) {
try {
if(socketClient != null) {
If socketClient is null this loop will spin meaninglessly. Surely this method should construct the socket client?
String message = socketClient.getMessage();
logger.info ("Message size:" + message.length ());
Here you are failing to check for null and failing to respond appropriately, which would be to close the socket and exit the loop. Instead you will get an NPE here.
if(!message.equals("EmptyString")) {
process(message);
See above. Don't send yourself special text messages. What happens if the peer needs to send that one day?
}
}
} catch (Exception exception) {
logger.info("Unable to read the socket message" +exception);
Unacceptable. This catch is inside the loop and it essentially ignores the exception. The result is that, again, this loop will spin meaninglessly on any exception. And the methods you're calling should be declared to throw IOException, and that is all you should catch here. At present you will spin even on NullPointerException.

Handling multi Java TCP clients with Threads

I have been working with TCP server/client stuff for a while. I am actully good at UDP programming when it comes to connecting more than one user that is multiple clients. I tried to do the same on a TCP server that i made using Threads but whenever the Thread gets to this piece of code
String reader = (String)in.readObject();
an error is generated and the thread stops executing the code but the thread still runs the program keeping it alive.
Anyway here is the entire source code :
public class TestServer implements Runnable {
private Thread run, streams, connect, receive, send;
private ServerSocket socket;
private Socket conn;
private ObjectInputStream in;
private ObjectOutputStream out;
private boolean running, incomingMessage = false;
private int port;
public TestServer(int port) throws IOException {
this.port = port;
socket = new ServerSocket(port);
console("Server stated on : " + InetAddress.getLocalHost() + " : " + port);
run = new Thread(this, "Run");
run.start();
}
public void run() {
running = true;
connect();
receive();
}
private void connect() {
connect = new Thread("Connect") {
public void run() {
while(running) {
try {
conn = socket.accept();
} catch (IOException e) {
e.printStackTrace();
}
console("You are now connected" + conn.getInetAddress().toString() + " : " + conn.getPort());
try {
setupStreams();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}; connect.start();
}
private void setupStreams() throws IOException {
streams = new Thread("Streams") {
public void run() {
try {
console("Setting up Streams");
out = new ObjectOutputStream(conn.getOutputStream());
out.flush();
in = new ObjectInputStream(conn.getInputStream());
console("Streams are now setup");
incomingMessage = true;
receive.start();
} catch(IOException e) {
e.printStackTrace();
}
}
}; streams.start();
}
private void receive() {
receive = new Thread("Receive") {
public void run() {
while(incomingMessage) {
String message = "";
try {
message = (String) in.readObject();
//This is the only flaw the program
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
console("Client : " + message);
}
}
};
}
private void console(String message) {
System.out.println(message);
}
public static void main(String[] args) {
try {
new TestServer(1234);
} catch (IOException e) {
e.printStackTrace();
}
}
}
FYI am not new to this. The error is caused because the server starts receiving packets even when there are no packets to be received. But because the thread forces it to receive it, i generates the error in the thread and dont know any other way to counter this. So please help. Thanks in Advance.
You shouldn't need 2 threads per connection. One thread is all that's required. After the connection is accepted, pass it to a worker thread to start reading. This can be done in a while loop in the worker thread.
Even though the socket's input stream can be read, the ObjectInputStream() class is more sensitive. If there is any error, its state is corrupted and it can't be used.
while (true) {
try {
Object input = in.readObject();
message = (String) input;
} catch (IOException e) {
e.printStackTrace();
break; //unrecoverable
} catch (ClassNotFoundException e) {
e.printStackTrace();
break; //unrecoverable
}
console("Client : " + message);
}
It's a better design to use a specific message protocol instead of sending serialized Java objects. For example if you are sending Strings like your sample, an InputStreamReader can be used to convert bytes to characters more easily and with less error handling.
These resources would be helpful to you:
https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html#later
Java - Listening to a socket with ObjectInputStream
ObjectInputStream(socket.getInputStream()); does not work

Android client-server communication throws exception on input.read

i am developing Android-Server communication using sockets and input,output streams (no wrappign classes like datainputstream, objectinput stream etc.)
Communiction is based on three threads on each side (server has additional thread which accepts new socket)
1st thread is director, which accepts messages from receiver via LinkedBlockingQueue, reacts on them and sends data via LinkedBlockingQueue to sender
2nd thread is receiver which periodically reads socket (via InputStream.read) and if there is message it pass it to director with LinkedBlockingQueue
when connection is lost, Client-Android device (which has input.read blocked) throws immediately connection time out exception
3rd thread is sender which periodically takes messages from LinkedBlockingQueue and sends that data to other side of connection
Problem is: Prevent Exception on ClientSide receiver from throws (this look alike some Android thing, because input.read alone should not throw any exception related to time out connection
here is code of receiver:
public class Receiver implements Runnable {
private boolean run = true;
BlockingQueue<MessageQueue> queueReceiverOut;
InputStream in;
////////////////////////////// CONSTRUCTOR ////////////////////////////////
public Receiver(BlockingQueue<MessageQueue> queueReceiverOut, InputStream in) {
this.queueReceiverOut = queueReceiverOut;
this.in = in;
}
// ////////////////////////////// METHODS ////////////////////////////////
/**
* Runs when thread starts.
*/
public void run() {
int[] message = new int[2];
byte[] data;
MessageQueue msg;
try {
while(true) {
msg = new MessageQueue();
message = receiveMessage();
System.out.println("receives message");
if(message[0] != -1) {
System.out.println("receives full message");
if(message[1] != 0) {
data = receiveData(message[1]);
msg.setMessageType(message[0]);
msg.setDataLength(message[1]);
msg.setData(data);
queueReceiverOut.put(msg);
} else {
msg.setMessageType(message[0]);
msg.setDataLength(message[1]);
queueReceiverOut.put(msg);
}
}
}
} catch (IOException e) {
System.out.println("----disconnected-----");
try {
MessageQueue msgReceiverOut = new MessageQueue();
msgReceiverOut.setMessageType(SocketMessages.STATUS_OFFLINE);
queueReceiverOut.put(msgReceiverOut);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int[] receiveMessage() throws IOException {
int[] messageHead = new int[2];
messageHead[0] = in.read();
if(messageHead[0] != -1) {
System.out.println("received message with type : " + messageHead[0]);
int length1 = in.read();
int length2 = in.read();
int length3 = in.read();
int length4 = in.read();
messageHead[1] = ((length1 << 24) + (length2 << 16) + (length3 << 8) + (length4 << 0));
System.out.println(" with length : " + messageHead[1]);
}
return messageHead;
}
public byte[] receiveData(int length) throws IOException {
byte[] buffer = new byte[length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < length
&& (numRead = in.read(buffer,
offset, length - offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < length) {
throw new IOException("Could not completely read file ");
}
return buffer;
}
public boolean isRun() {
return run;
}
public void setRun(boolean run) {
this.run = run;
}
}
and sender:
public class Sender implements Runnable {
private boolean run = true;
BlockingQueue<MessageQueue> queueSenderIn;
BlockingQueue<MessageQueue> queueSenderOut;
OutputStream out;
////////////////////////////// CONSTRUCTOR ////////////////////////////////
public Sender(BlockingQueue<MessageQueue> queueSenderIn, BlockingQueue<MessageQueue> queueSenderOut, OutputStream out) {
this.queueSenderOut = queueSenderOut;
this.queueSenderIn = queueSenderIn;
this.out = out;
}
// ////////////////////////////// METHODS ////////////////////////////////
/**
* Runs when thread starts.
*/
#Override
public void run() {
MessageQueue msg;
try {
while(run) {
msg = queueSenderIn.poll(2, TimeUnit.SECONDS);
if(msg != null) {
sendMessage(msg.getMessageType(),msg.getDataLength());
if(msg.getDataLength()!=0) {
sendData(msg.getData());
}
}
}
Log.v(getClass().getName(),"sender destroyed");
} catch (IOException e) {
Log.v(getClass().getName(),"connection closed");
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void sendMessage(int messageType, int dataLength) throws IOException, InterruptedException {
MessageQueue msg = new MessageQueue();
Log.v(getClass().getName(), "sending message type : " + messageType);
out.write(messageType);
Log.v(getClass().getName(), "sending data with length : " +dataLength);
out.write((dataLength >>> 24) & 0xFF);
out.write((dataLength >>> 16) & 0xFF);
out.write((dataLength >>> 8) & 0xFF);
out.write((dataLength >>> 0) & 0xFF);
msg.setMessageType(messageType);
queueSenderOut.put(msg);
}
public void sendData(byte[] data) throws IOException {
String string = new String(data,"UTF-8");
Log.v(getClass().getName(), " with content : " + string);
out.write(data);
}
public boolean isRun() {
return run;
}
public void setRun(boolean run) {
this.run = run;
}
}
UPDATED : Because misinterpreting exception
Under abnormal conditions the underlying connection may be broken by
the remote host or the network software (for example a connection
reset in the case of TCP connections). When a broken connection is
detected by the network software the following applies to the returned
input stream :
The network software may discard bytes that are buffered by the
socket. Bytes that aren't discarded by the network software can be
read using read.
If there are no bytes buffered on the socket, or all buffered bytes
have been consumed by read, then all subsequent calls to read will
throw an IOException.
If there are no bytes buffered on the socket, and the socket has not
been closed using close, then available will return 0.
from: http://docs.oracle.com/javase/7/docs/api/java/net/Socket.html#getInputStream()
Especially mind the "subsequent calls" part. That means if you are already blocking in a read call, the above condition regarding read calls does not (yet) apply.
So far the explanation. Now to the solution:
You can (one of many possibilities) periodically send messages, even when communication is idle. So your sender will detect connection loss and can close the in-stream, too.
EDIT: To make it a little bit clearer ...
Connection loss
call read
IOException
whereas
call read (blocks!)
waiting for input: connection lost
-- no Exception!
I guess the point is that ( I assume ) you server enters a read and stays there for a long time, while your client is receiving data while connection is broken. Thus it will constantly call and return from read. It still is possible that the connection loss is happening while waiting for a read to unblock, but it is much less probable.

Java thread stuck with notify call

i'm stuck with this code, and i think that i'm missing something really important.
when the thread is running i can add message to the vector
but when it calls notify it seems that the getNextMessageFromQueue() keep to stay on the wait.
Am i locking the messages vars?
Thanks for your help.
My dispatcher class which send all messages to my clients :
private Vector<Message> messages = new Vector<Message>();
public synchronized void addMessage(Message message) {
messages.add(message);
notify();
}
private synchronized Message getNextMessageFromQueue() throws InterruptedException {
while (messages.size() < 1) {
wait();
}
Message message = (Message) messages.get(0);
messages.removeElementAt(0);
return message;
}
private void sendMessageToAllClients(Message message) {
for (int i=0; i < clients.size(); i++) {
Client client = (Client) clients.get(i);
client.sendMessage(message);
}
}
public void run() {
try {
while (true) {
Message message = getNextMessageFromQueue();
sendMessageToAllClients(message);
}
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
Here the client class :
private Socket socket;
private ObjectOutputStream out;
private ObjectInputStream in;
public Client(Socket s) throws IOException {
socket = s;
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
}
public Socket getSocket() {
return socket;
}
public void sendMessage(Message message) {
try {
out.writeObject(message);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
Here is the main call of addMessage :
Message message = new Message();
message.setMessage("Welcome to " + client.getSocket().getLocalAddress() + ":" + client.getSocket().getPort());
dispatcher.addMessage(message);
I think you have a mistake in line in = new ObjectInputStream(socket.getInputStream());
Remove it, if it is not necessary or rebuild in other way. Read this Java sockets: Program stops at socket.getInputStream() w/o error?
To understand if your inputData is empty use - socket.getInputStream().available(), it returns size of input bytes.

Categories