I'm having a bit of trouble finding the cause of my problem.
Functionality of the program is as follows... the Server
allows multiple users to log in (connect to the server) and edit the same string variable named text
with the starting commands of either rep:
(for replace the whole string) or app:
(to append to the string).
When the client connects, they have to enter a command. Whatever the command is, it gets echoed back to them in the same window (running the server and client simultaneously in different CMD windows). So if they enter hello
, the echo would be ECHO: hello
.
If the command entered is rep:tight
, the text
string variable in the server should change to contain tight
and should then be returned back/ displayed back in the client cmd window as tight
- without the ECHO.
If the command after that is app:rope
, the text
string variable in the server should change to contain tightrope
and should be returned back/ displayed back in the client cmd window as tightrope
.
In addition, the user cannot enter any value under 4 characters and thus SynchClient should display an error message and prompt the user to enter another value.
The issue I'm having is the fact that my echoed values are not changing after every new input. I get the return of the first entered command and that's it and I'm struggling to get my head around it all.
EDIT: Might be best if you ran the program yourself.
Here is what my SynchServer.java file looks like:
import java.io.*;
import java.net.*;
import java.util.*;
public class SynchServer
{
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = null;
final int PORT = 1234;
Socket client;
ClientHandler handler;
try
{
serverSocket = new ServerSocket(PORT);
}
catch (IOException ioEx)
{
System.out.println("\nUnable to set up port!");
System.exit(1);
}
System.out.println("\nServer running...\n");
do
{
//Wait for client.
client = serverSocket.accept();
System.out.println("\nNew client accepted.\n");
handler = new ClientHandler(client);
handler.start();
}while (true);
}
}
class ClientHandler extends Thread
{
private Socket client;
private Scanner input;
private PrintWriter output;
private static String text = "";
public ClientHandler(Socket socket) throws IOException
{
client = socket;
input = new Scanner(client.getInputStream());
output = new PrintWriter(client.getOutputStream(),true);
}
public void run()
{
String head, tail, received;
received = input.nextLine();
head = received.substring(0, 4);
tail = received.substring(4);
while (!received.equals("QUIT"))
{
if (head.equals("rep:"))
changeText(tail);
else
if (head.equals("app:"))
appendText(tail);
output.println(text);
output.println("ECHO: " + received);
}
try
{
System.out.println("Closing down connection...");
client.close();
}
catch(IOException ioEx)
{
System.out.println("* Disconnection problem! *");
}
}
private synchronized void changeText(String changedText)
{
text = changedText;
}
private synchronized void appendText(String appendedText)
{
text += appendedText;
}
}
and my SynchClient.java file:
import java.io.*;
import java.net.*;
import java.util.*;
public class SynchClient
{
public static void main(String[] args) throws IOException
{
InetAddress host = null;
final int PORT = 1234;
Socket socket;
Scanner networkInput,keyboard;
PrintWriter output;
try
{
host = InetAddress.getLocalHost();
}
catch(UnknownHostException uhEx)
{
System.out.println("\nHost ID not found!\n");
}
socket = new Socket(host, PORT);
networkInput = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(),true);
keyboard = new Scanner(System.in);
String message, response;
do
{
System.out.print("\nEnter message ('QUIT' to exit): ");
message = keyboard.nextLine();
if (message.length() < 4)
{
System.out.print("\nPlease enter a value greater than 4 characters: ");
message = keyboard.nextLine();
}
output.println(message);
if (!message.equals("QUIT"))
{
response = networkInput.nextLine();
System.out.println("\n" + response);
}
}while (!message.equals("QUIT"));
try
{
System.out.println("\nClosing down connection...\n");
socket.close();
}
catch(IOException ioEx)
{
System.out.println("\n* Disconnection problem! *\n");
}
}
}
EDIT 2: My Working Solution:
Server:
public class SynchServer
{
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = null;
final int PORT = 1234;
Socket client;
ClientHandler handler;
try
{
serverSocket = new ServerSocket(PORT);
}
catch (IOException ioEx)
{
System.out.println("\nUnable to set up port!");
System.exit(1);
}
System.out.println("\nServer running...\n");
do
{
//Wait for client.
client = serverSocket.accept();
System.out.println("\nNew client accepted.\n");
handler = new ClientHandler(client);
handler.start();
}while (true);
}
}
class ClientHandler extends Thread
{
private Socket client;
private Scanner input;
private PrintWriter output;
private static String text = "";
public ClientHandler(Socket socket) throws IOException
{
client = socket;
input = new Scanner(client.getInputStream());
output = new PrintWriter(client.getOutputStream(),true);
}
public void run()
{
String head, tail, received;
received = input.nextLine();
// create head and tail in case first input is rep: or app:
head = received.substring(0, 4);
tail = received.substring(4);
while (!received.equals("QUIT"))
{
if (head.equals("rep:"))
{
changeText(tail);
output.println(text);
// input for next one
}
else
if (head.equals("app:"))
{
appendText(tail);
output.println(text);
// get input for next
}
else
{
//must be some random thing that just needs to be echoed
output.println(text);
}
//Get next input
received = input.nextLine();
//and set the head and tail again
head = received.substring(0, 4);
tail = received.substring(4);
}
try
{
System.out.println("Closing down connection...");
client.close();
}
catch(IOException ioEx)
{
System.out.println("* Disconnection problem! *");
}
}
private synchronized void changeText(String changedText)
{
text = changedText;
}
private synchronized void appendText(String appendedText)
{
text += appendedText;
}
}
Client:
public class SynchClient
{
public static void main(String[] args) throws IOException
{
InetAddress host = null;
final int PORT = 1234;
Socket socket;
Scanner networkInput,keyboard;
PrintWriter output;
try
{
host = InetAddress.getLocalHost();
}
catch(UnknownHostException uhEx)
{
System.out.println("\nHost ID not found!\n");
}
socket = new Socket(host, PORT);
networkInput = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(),true);
keyboard = new Scanner(System.in);
String message, response;
do
{
System.out.print("\nEnter message ('QUIT' to exit): ");
message = keyboard.nextLine();
while (message.length() < 4)
{
System.out.print("\nPlease enter 4 or more characters: ");
message = keyboard.nextLine();
}
output.println(message);
if (!message.equals("QUIT"))
{
response = networkInput.nextLine();
System.out.println("\n" + response);
}
}while (!message.equals("QUIT"));
try
{
System.out.println("\nClosing down connection...\n");
socket.close();
}
catch(IOException ioEx)
{
System.out.println("\n* Disconnection problem! *\n");
}
}
}
This is not primarily about static
fields at all. Your mistake is in this loop:
while (!received.equals("QUIT")) {
if (head.equals("rep:"))
changeText(tail);
else if (head.equals("app:"))
appendText(tail);
output.println(text);
output.println("ECHO: " + received);
}
It runs infinitely after first client input and do not read new value from input stream each time.
These lines:
received = input.nextLine();
head = received.substring(0, 4);
tail = received.substring(4);
should be put into the loop:
while (!received.equals("QUIT")) {
received = input.nextLine();
head = received.substring(0, 4);
tail = received.substring(4);
...
UPDATE:
To be precise it has another, secondary problem, these two lines:
output.println(text);
output.println("ECHO: " + received);
They should be changed into just:
output.println("ECHO: " + text);
(otherwise you send two separate lines which is not desired).
You have a synchronized block that applies to each object separately, not every object, as synchronized block applies to objects not the whole class.
You may use a static lock, so all clients wait for their turn. Check this link to see the comparison between static and not static locks
Static versus non-static lock object in synchronized block
and this
How to lock a method for a whole class using synchronized?
So, you should eliminate static
modifier from text
field declaration:
private String text = "";
In this case you do not need synchronized
for changeText
and appendText
methods at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With