0

I'm developing a chat application, and I'm using global variables as a way to allow server threads to access and update shared information (I know this isn't recommended, but I'm going to stick with this method for the time being).

What I want to happen, is that every time a new message is submitted, it prints to all the clients so that all the clients can see them.

What actually happens, is that it only updates the client that I've submitted a message with. Though it does kinda of receive messages from the other client. It's not simultaneous.

Where am I going wrong?

class Server {

public static void main(String argv[]) throws Exception { 

    boolean listening = true;
    int portNumber = 6000;

    try (ServerSocket serverSocket = new ServerSocket(portNumber)) { 
        while (listening) {
            new ServerThread(serverSocket.accept()).start();
        }
    } catch (IOException e) {
        System.err.println("Could not listen on port " + portNumber);
        System.exit(-1);
    }
}
}

Server thread for handling clients.

public class ServerThread extends Thread {
private Socket socket = null;
public ServerThread(Socket socket) {
    super("ServerThread");
    this.socket = socket;
}

public void run () {

    int msgCnt = 0;
    boolean running = true;

    try (
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(
            new InputStreamReader(
                socket.getInputStream()));
    ) {
        String inputLine, outputLine;

        while(running) {



        while ((inputLine = in.readLine()) != null) {
            if (Global.messageCount > msgCnt){
                out.println(Global.currentMessage + " received from others");
                msgCnt = msgCnt + 1;
            }
            outputLine = inputLine;
            Global.currentMessage = inputLine;
            Global.messageCount = Global.messageCount + 1;
            //out.println(outputLine);


        }
            //if (outputLine.equals("QUIT"))
            //    break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

Also, client code

class Client {
public static void main(String argv[]) throws Exception { 
    String sentMessage;  //variable for input
    String status;
    boolean running;

    BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));

    Socket clientSocket = new Socket("127.0.0.1", 6000); //name of computer to connect with and port number to use

    DataOutputStream outToServer = 
            new DataOutputStream(clientSocket.getOutputStream());
    BufferedReader inFromServer = 
            new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

    running = true;

    while(running)
    {
    sentMessage = inFromUser.readLine(); //user inputs text to variable 'sentMessage'

    outToServer.writeBytes(sentMessage + '\n'); //the variable is sent to the server

    status = inFromServer.readLine(); //the modified data is returned from the server

    System.out.println("FROM SERVER: " + status); //display to user

    }     
clientSocket.close();     
}
}

Global class, just a few variables

public class Global {
    public static String currentMessage = "Nothing";
    public static int messageCount = 0;
}
serhio
  • 28,010
  • 62
  • 221
  • 374

3 Answers3

1

I think your error is in this logic here:

    while ((inputLine = in.readLine()) != null) {
        if (Global.messageCount > msgCnt){
            out.println(Global.currentMessage + " received from others");
            msgCnt = msgCnt + 1;
        }
        outputLine = inputLine;
        Global.currentMessage = inputLine;
        Global.messageCount = Global.messageCount + 1;
    }

Notice that you are only going to begin executing the contents of this while loop once a client has submitted a message. If the thread is blocked on in.readLine() and another thread submits a message, there is nothing to cause this while loop to kick off. I would expect you would get the output you want on the next message submitted by this client.

One other note: it seems like you should set msgCnt = Global.messageCount, otherwise you could have a situation where Global.messageCount is more than +1 of the current msgCnt, and then you forevermore run the contents of your if (Global.messageCount > msgCnt) statement.

Sean Adkinson
  • 8,425
  • 3
  • 45
  • 64
  • Thank you. I changed the loop, and it definitely fixed something. The clients still only receive feedback on their own messages though. Not from other clients. I'm baffled. – WorldsWorstProgrammer Apr 23 '14 at 15:51
0

You need to maintain the list of all the clients.

If any message is received from any client just iterate through the list and send the messages to other clients.

Sample code:

List<ServerThread> allClients=new ArrayList<ServerThread>();

class ServerThread extends Thread {
   private Socket socket = null;
   private PrintWriter out;

    public ServerThread(Socket socket) {
        ...
        allClients.add(this);
    }
   ...

       ...
       //code where you received any message
       for (ServerThread client : allClients) {
             if(client != this) {
                  client.out.println(Global.currentMessage + " received from others");
             }
       }
       ...
}

I have posted a sample code in the same context.

Please have a look at Java Server with Multiclient communication. and follow the post to find more samples.

Community
  • 1
  • 1
Braj
  • 46,415
  • 5
  • 60
  • 76
0

From Java Concurrency in Practice by Brian Goetz:

[...] in the absence of synchronization, there are a number of reasons a thread might not immediately -- or ever -- see the results of an operation in another thread. Compilers may generate instructions in a different order than the "obvious" one suggested by the source code, or store variables in registers instead of in memory; processors may execute instructions in parallel or out of order; caches may vary the order in which writes to variables are committed to main memory; and values stored in processor-local caches may not be visible to other processors. These factors can prevent a thread from seeing the most up-to-date value for a variable and can cause memory actions in other threads to appear to happen out of order -- if you don't use adequate synchronization.

Declare Global.currentMessage to be volatile. Also, use AtomicInteger for Global.messageCount, calling AtomicInteger.getAndIncrement() to increment the counter.

You will also encounter other concurrency-related issues with your design. I suggest reading up on concurrency in Java before continuing. The aforementioned book is a most excellent resource.

Chuck Batson
  • 2,165
  • 1
  • 17
  • 15