Why is this while loop not terminating? - java

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.

Related

How too allow a client on a server to send multiple messages? JAVA

I've been making a chat room where multiple clients can connect and talk together on the same server. The only problem I'm having is getting each client to send more than one message. I've been trying different ways of looping the method to do so but I'm having some issues.
Any help would be appreciated :) thank you.
HERE'S THE CODE:
public class Client {
public static void main(String[] args){
Scanner clientInput = new Scanner(System.in);
try {
Socket SOCK = new Socket("localhost", 14001);
System.out.println("Client started!");
//Streams
while(true){
OutputStream OUT = SOCK.getOutputStream(); //writing data to a destination
PrintWriter WRITE = new PrintWriter(OUT); // PrintWriter prints formatted representations of objects to a text-output stream
InputStream in = SOCK.getInputStream(); //reads data from a source
BufferedReader READ = new BufferedReader(new InputStreamReader(in));
//---------------------------------
System.out.print("My input: ");
String atServer = clientInput.nextLine();
WRITE.write(atServer + "\n");
WRITE.flush(); //flushes the stream
String stream = null;
while((stream = READ.readLine()) != null){ //if stream is not empty
System.out.println("Client said: " + stream);
}
READ.close();
WRITE.close();
}
} catch (UnknownHostException e){
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
I've tried using a while loop to continuously ask for an input but doesn't seem to be working.
Are you making it out of the READ.readLine() while loop? Perhaps you're never getting an end of input character and thats never terminating. Also, you're closing both your READ and WRITE at the end of the while loop, and then expect them to be open on the next iteration. Move those and the close statements to the same layer as the Socket.
With that, every time you send something, your client is expecting something in response from the server. If you don't want them to be dependent on each other, I recommend moving the receive logic to its own thread in a while(true) loop.

How to properly stream data from a socket with Java

I am attempting stream data over a socket with Java in an attempt to write a Kafka producer. I've written a class to pull the data in but I'm not getting the results I'd expect. I've got it set up so the data is being streamed from a Linux box. The source of the data is a csv file that I'm using the nc utility to stream. The class is running on a Windows 10 machine from Eclipse. When I run the class I see two weird things.
The column headers don't get transmitted.
I can only run the class once. If I want to run it again, I have to stop nc and restart it.
Below is my code. Am I missing anything? At this point I'm just trying to connect to the socket and pull the data over.
I run nc with the following command:
$ nc -kl 9999 < uber_data.csv
Below is my class
import java.net.*;
import java.io.*;
public class Client
{
static String userInput;
public static void main(String [] args)
{
try
{
InetAddress serverAddress = InetAddress.getByName("servername");
Socket socket = new Socket(serverAddress, 9999);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while ((userInput = input.readLine()) != null) {
System.out.println(input.readLine());
}
input.close();
socket.close();
}
catch(UnknownHostException e1)
{
System.out.println("Unknown host exception " + e1.toString());
}
catch(IOException e2)
{
System.out.println("IOException " + e2.toString());
}
catch(IllegalArgumentException e3)
{
System.out.println("Illegal Argument Exception " + e3.toString());
}
catch(Exception e4)
{
System.out.println("Other exceptions " + e4.toString());
}
}
}
You're throwing away every odd-numbered line. It should be:
while ((userInput = input.readLine()) != null) {
System.out.println(userInput);
}
Secondly, you aren't closing the socket. Use a try-with-resources:
try
{
InetAddress serverAddress = InetAddress.getByName("servername");
try (
Socket socket = new Socket(serverAddress, 9999);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
while ((userInput = input.readLine()) != null) {
System.out.println(input.readLine());
}
}
}
catch (...)
First, each call readLine() tries to read line from input stream.
In userInput = input.readLine() you read header, but println(input.readLine()) read body and print in console.
while ((userInput = input.readLine()) != null) {
System.out.println(userInput); //instead input.readLine()
}
Second, I didn't use nc, but I think problem will solve if you will close socket (and reader) in finally statement.
I hope it would be helpful.
For the first question: you were trying to print userInput string. But it's printing the result of another readline() call.
For the second: after the file has been transferred, you have to stop and restart nc; no matter what you do from your side. It's from nc side.
See the nc documentation.

Socket server stream messages in stream are mixed

I am implementing multiplayer game with server socket mechanism.
Game is running in one loop mechanism.
Server only broadcast any message to every player connected.
Server creates thread for every connected player
Messages are JSONs and they are being sent every 100ms from client side.
Messages are being broadcasted by server and read by client without any interval.
This is server loop:
while (true) {
try {
System.out.println("LISTENING...");
Socket clientSocket = serverSocket.accept();
System.out.println("GOT CONNECTION");
PlayerThread playerThread = new PlayerThread(clientSocket);
playerThread.start();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Exception occured on server");
break;
}
}
This is PlayerThread loop:
while (true) {
try {
String inputMessage = in.readUTF();
System.out.println("GOT: " + inputMessage);
JsonNode jsonNode = objectMapper.readTree(inputMessage);
String senderName = jsonNode.get("senderName").asText();
if (!players.contains(this)) {
playerName = senderName;
players.add(this);
}
for (PlayerThread p : players) {
p.getOut().writeUTF(inputMessage);
}
And finally listening to messages:
public void listen() {
if (connected) {
try {
if (in.available() > 0) {
String inputMessage = in.readUTF();
System.out.println("GOT MESSAGE: " + inputMessage);
handleMessages(inputMessage);
}
} catch (IOException e) {
LOGGER.log(Level.INFO, "Connection lost");
connected = false;
}
} else
LOGGER.log(Level.INFO, "Player is not connected");
}
Method above is run in main game loop. It checks if there's something in inputStream and then reads it.
This is how correct message looks like:
GOT MESSAGE: {"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 52","posX":10.0,"posY":5.0}
It works ok when there are 2 players, but the more players connect the more probably is to get message like this (or similiar broken messages):
GOT MESSAGE: 0} U{"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 65","posX":10.0,"posY":5.0}
or
GOT MESSAGE: {"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 24","pos
There different errors in message like Y letter, half-message or multiple messages in one row. Why such thing happen? It looks like when there are more players and they write into output stream in server side, this stream is not read yet so they are appending and appending. But it doesn't explain why there are broken and most imporant, how to resolve it?
I can move reading stream to another thread because in.readUTF() locks process but I wanted to keep it synchronized in main game loop and I don't think this will help (am I wrong?)
You need to synchronize your write loop on an object that's common between all PlayerThreads so that messages don't interleave.
synchronized(/*Should be a "global" server object*/) {
for (PlayerThread p : players) {
p.getOut().writeUTF(inputMessage);
}
}

How to add heartbeat messaging on top of this Java code( for KnockKnockClient/Server)?

I'm studying the following basic Java socket code( source ). It's a Knock-Knock-Joke client/server app.
In the Client, we set up the socket as usual:
try {
kkSocket = new Socket("localhost", 4444);
out = new PrintWriter(kkSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
} catch( UnknownHostException uhe ){ /*...more error catching */
And then later, we just read and write to Server:
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String fromServer;
String fromUser;
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("bye."))
break;
fromUser = stdIn.readLine();
if (fromUser != null){
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
And on the server, we have the corresponding code, to get the joke punch-line.
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye."))
break;
I want to attach a heartbeat to the whole thing, which will print out to the console whenever it detects that the other side died. Because what happens now if I kill the other side is an exception - like this one below:
So if I am running both KnockKnockClient and KnockKnockServer, then I shut down KnockKnockServer, what should happen is that on the Client I see this outputted:
>The system has detected that KnockKnockServer was aborted
I'm looking for any tips. So far I've mainly been trying to run a daemon thread that periodially creates new connections to the other side. But I'm confused about what condition to check for(but I think it's just a boolean value?). Is that the right approach? I just found out online there's a library called JGroups for multicast networking - would that be a better way? I'm looking for any tips.
My server-code so far(sorry it's messy)
&
Client-side
thanks
But the exception you are getting is exactly this! It's telling you that the other side just died. Just catch the exception and print to the console, that "The system has detected that KnockKnockServer was aborted".
You are using TCP connection and TCP has built-in heartbeat (keepalive) mechanism that will do this for you. Just set setKeepAlive() on the socket. That being said - It is possible to control keepalive frequency per each connection, but I do not know how to do that in java.
http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
https://stackoverflow.com/a/1480259/706650
you have a Synchronous communication. for having the heartbeat message, use an asynchronous communication. there will be 2 threads. one will read from the socket and another will keep writing to the socket. If you use asynchronous communication, the server will be sending a message every 10 seconds. the client thread will be reading messages from the server and if there is no message, it means the server is down. in your case, the server either sends back the message to client(if client has some message) or send an automatic reply.your server code can be modified like this.
Create a server thread that will keep sending messages to client every 10 seconds.
public class receiver extends Thread{
public static bool hearbeatmessage=true;
Socket clientSocket=new Socket();
PrintWriter out=new PrintWriter();
public receiver(Socket clientsocket){
clientSocket=clientsocket;
out = new PrintWriter(clientSocket.getOutputStream(), true);
}
public void run(){
while(true)
{
if(heartbeatmessage){
thread.sleep(10000);
out.println("heartbeat");
}
}
}
}
In your server code:
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
receiver r=new reciver(clientSocket);
r.run(); /*it will start sending hearbeat messages to clients */
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
reciver.hearbeatMessage=false; /* since you are going to send a message to client now, sending the heartbeat message is not necessary */
out.println(outputLine);
reciver.hearbeatMessage=true; /*start the loop again*/
if (outputLine.equals("Bye."))
break;
The client code will also be modified, a thread will keep reading messages from the socket and if it has not received message for more than 11 seconds(1 second extra), it will declare the server is not available.
Hope this helps. There might be some flaw in the logic too. Let me know.
The following are best practices which we apply on a daily base when interfacing with hardware (using sockets).
Good practice 1 : SoTimeout
This property enables a read timeout. The goal of this is to avoid the issue that Tom had. He wrote something in the line of : "you will need to wait till the next client message arrives". Well, this offers a solution to that problem. And it's also the key to implementing a heartbeat and many other checks.
By default, the InputStream#read() method will wait forever, until a message arrives. The setSoTimeout(int timeout) changes this behaviour. It will apply a timeout now. When it timeouts it will throw the SocketTimeoutException. Just catch the exception, check a couple of things and continue reading (repeat). So basically, you put your reading method in a loop (and probably even in a dedicated thread).
// example: wait for 200 ms
connection.setSoTimeout(200);
You can use these interruptions (caused by the timeout) to validate the status: E.g. how long has it been since I received my last message.
Here is an example to implement the loop:
while (active)
{
try
{
// some function that parses the message
// this method uses the InputStream#read() method internally.
code = readData();
if (code == null) continue;
lastRead = System.currentTimeMillis();
// the heartbeat message itself should be ignored, has no functional meaning.
if (MSG_HEARTBEAT.equals(code)) continue;
//TODO FORWARD MESSAGE TO ACTION LISTENERS
}
catch (SocketTimeoutException ste)
{
// in a typical situation the soTimeout should be about 200ms
// the heartbeat interval is usually a couple of seconds.
// and the heartbeat timeout interval a couple of seconds more.
if ((heartbeatTimeoutInterval > 0) &&
((System.currentTimeMillis() - lastRead) > heartbeatTimeoutInterval))
{
// no reply to heartbeat received.
// end the loop and perform a reconnect.
break;
}
// simple read timeout
}
}
Another use of this timeout: It can be used to cleanly stop your session by setting active = false. Use the timeout to check if this field is true. If that's the case, then break the loop. Without the SoTimeout logic this would not be possible. You would either be forced to do a socket.close() or to wait for the next client message (which clearly makes no sense).
Good practice 2 : Built-in Keep-Alive
connection.setKeepAlive(true);
Well basically this is pretty much what your heart-beat logic does. It automatically sends a signal after a period of inactivity and checks for a reply. The keep-alive interval is operating system dependent though, and has some shortcomings.
Good practice 3 : Tcp No-Delay
Use the following setting when you are often interfacing small commands that need to be handled quickly.
try
{
connection.setTcpNoDelay(true);
}
catch (SocketException e)
{
}
I think you are over complicating things.
From the client side:
If the client gets an IOException for the connection reset, then this means the server is dead. Instead of printing the stack trace just do what ever you need to do once you know that the server is down. You already know the server is down due to the exception.
From the server side:
Either start a timer and if you don't get a request for a time more than the interval assume that the client is down.
OR start a background server thread at the client (making the client and server peers) and have the server send a "dummy" hearbeat request (server now acts as a client). If you get exception the client is down.
Figured I'd take a crack at this... I started with the KnockKnockServer and KnockKnockClient that are on the Java site (in your original question).
I didn't add any threading, or heartbeats; I simply changed the KnockKnockClient to the following:
try { // added try-catch-finally block
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
} catch (java.net.SocketException e) { // catch java.net.SocketException
// print the message you were looking for
System.out.println("The system has detected that KnockKnockServer was aborted");
} finally {
// this code will be executed if a different exception is thrown,
// or if everything goes as planned (ensure no resource leaks)
out.close();
in.close();
stdIn.close();
kkSocket.close();
}
This seems to do what you want (even though I modified the original Java website example, rather than your code - hopefully you'll be able to see where it plugs in). I tested it with the case you described (shut down the server while the client is connected).
The downside to this is that, while the client is waiting for user input, you don't see that the server has died; you have to enter client input, and then you'll see that the server has died. If this is not the behavior you want, please post a comment (perhaps that was the whole point of the question - it just seemed like you might have been going down a longer road than you needed in order to get to where you wanted to be).
Here's a slight modification to the client. It doesn't use an explicit heartbeat, but as long as you keep reading from the server, you'll immediately detect the disconnect anyway.
This is because readLine will immediately detect any read errors.
// I'm using an anonymous class here, so we need
// to have the reader final.
final BufferedReader reader = in;
// Decouple reads from user input using a separate thread:
new Thread()
{
public void run()
{
try
{
String fromServer;
while ((fromServer = reader.readLine()) != null)
{
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
{
System.exit(0);
}
}
}
catch (IOException e) {}
// When we get an exception or readLine returns null,
// that will be because the server disconnected or
// because we did. The line-break makes output look better if we
// were in the middle of writing something.
System.out.println("\nServer disconnected.");
System.exit(0);
}
}.start();
// Now we can just read from user input and send to server independently:
while (true)
{
String fromUser = stdIn.readLine();
if (fromUser != null)
{
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
In this case, we allow client writes even when we're waiting for reply from the server. For a more stable application, we'd want to lock the input while we're waiting for a reply by adding a semaphore controlling when we start reading.
These are the modifications we would make to control the input:
final BufferedReader reader = in;
// Set up a shared semaphore to control client input.
final Semaphore semaphore = new Semaphore(1);
// Remove the first permit.
semaphore.acquireUninterruptibly();
new Thread()
... code omitted ...
System.out.println("Server: " + fromServer);
// Release the current permit.
semaphore.release();
if (fromServer.equals("Bye."))
... code omitted ...
while (true)
{
semaphore.acquireUninterruptibly();
String fromUser = stdIn.readLine();
... rest of the code as in the original ...
I think #Bala's answer is correct on server side. I'd like to give a supplementary on client side.
On client side, you should:
use an variable to keep the timestamp of the last message from server;
start a thread which runs periodically(every 1 second, e.g.) to compare current timestamp and the last message timestamp, if it is longer than desired timeout(10 seconds, e.g.), a disconnection should be reported.
Following are some code snippet:
The TimeoutChecker class(thread):
static class TimeoutChecker implements Runnable {
// timeout is set to 10 seconds
final long timeout = TimeUnit.SECONDS.toMillis(10);
// note the use of volatile to make sure the update to this variable thread-safe
volatile long lastMessageTimestamp;
public TimeoutChecker(long ts) {
this.lastMessageTimestamp = ts;
}
#Override
public void run() {
if ((System.currentTimeMillis() - lastMessageTimestamp) > timeout) {
System.out.println("timeout!");
}
}
}
Start the TimeoutChecker after connection is established:
try {
kkSocket = new Socket("localhost", 4444);
// create TimeoutChecker with current timestamp.
TimeoutChecker checker = new TimeoutChecker(System.currentTimeMillis());
// schedule the task to run on every 1 second.
ses.scheduleAtFixedRate(, 1, 1,
TimeUnit.SECONDS);
out = new PrintWriter(kkSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
} catch( UnknownHostException uhe ){ /*...more error catching */
The ses is a ScheduledExecutorService:
ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
And remember to update the timestamp when receiving messages from server:
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String fromServer;
String fromUser;
while ((fromServer = in.readLine()) != null) {
// update the message timestamp
checker.lastMessageTimestamp = System.currentTimeMillis();
System.out.println("Server: " + fromServer);
if (fromServer.equals("bye."))
break;
Adel,was looking at your code http://pastebin.com/53vYaECK
Can you try the following solution. not sure whether it will work.
instead of creating a bufferedreader with the inputstream once,
we can create an instance of BufferedReader eachtime.
when the kkSocket.getInputStream is null, it comes out of the while loop and set completeLoop to false, so that we exit the while loop.
it has 2 while loops and the objects are created each time.
if the connection is open but does not have data in it inputstream will not be null,
BufferedReader.readLine would be null.
bool completeLoop=true;
while(completeLoop) {
while((inputstream is=kkSocket.getInputStream())!=null) /*if this is null it means the socket is closed*/
{
BufferedReader in = new BufferedReader( new InputStreamReader(is));
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
}
completeLoop=false;
System.out.println('The connection is closed');
}

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.

Categories