1

I am currently having some problems when programming a client-server using sockets where I want multiple clients to be connected at the same time. The problem is that I have a client-GUI that I want to change the color of a circle whenever the server sends a signal to do so.

I am using JavaFX in the client-window.

The problem I have is that the server runs through the connection (using a thread) and does all the changes to the circle after it's done, whereas I would like it to be done dynamically.

This is my clientserver-thread-code. It sends parameters to the socket which the client picks up, and shall display the colour accordingly, and then sleep for a fixed amount of time before changing the colour of another circle. I also have a server-class which starts up the server with a given portnumber and is continuously listening for clients.

 class ClientServer extends Thread
{
Socket connectSocket;
InetAddress clientAddr;
String parametersFromClient;
char[] params;


public ClientServer(Socket connectSocket)
{
    this.connectSocket = connectSocket;
    clientAddr = connectSocket.getInetAddress();
}

public void run()
{
    try (

           //writer to the connection socket
            PrintWriter output = new PrintWriter(connectSocket.getOutputStream(), true);

            // Stream reader from the connection socket
            BufferedReader input = new BufferedReader(new InputStreamReader(connectSocket.getInputStream()));
    )
    {
          int[] lights = MultipleClientsServer.lightvalues;
        while (((parametersFromClient = input.readLine()) != null) ) //FIKS!!!
        {
            if(parametersFromClient.equals("connect"))
            {
            System.out.println(parametersFromClient);
            output.println("red");
            try {TimeUnit.SECONDS.sleep(lights[0]);} catch (InterruptedException e) {}

            output.println("yellow");
            try {TimeUnit.SECONDS.sleep(lights[1]);} catch (InterruptedException e) {}

            output.println("green");
            try {TimeUnit.SECONDS.sleep(lights[2]);} catch (InterruptedException e) {}

            output.println("");
            }
        } 

        System.out.println(MultipleClientsServer.IPList.toString());
        MultipleClientsServer.removeFromList(clientAddr.toString());
        // close the connection socket
        connectSocket.close();
        //søke etter inetadress i linkedlist for å fjerne
        System.out.println(MultipleClientsServer.IPList.toString());


    } catch (IOException e)
    {
        System.out.println("Exception occurred when trying to communicate with the client " + clientAddr.getHostAddress());
        System.out.println(e.getMessage());
    }
}

In my client-code I have a connection-method which starts up whenever I push a button: (red, yellow, and green are the circles I'd like to change the color of.)

public static Socket clientStart()
 {
   String hostName = "127.0.0.1"; // Default host, localhost
  int portNumber = 5555; // Default port to use

     try
    (
        // create TCP socket for the given hostName, remote port PortNumber
        Socket echoSocket = new Socket(hostName, portNumber);

    )
     {
         String s = "";
      System.out.println("Sender til ip: " + hostName + " port: " + portNumber);
      // Stream writer to the socket
       out = new PrintWriter(echoSocket.getOutputStream(), true);
      // Stream reader from the socket
       in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); 
       out.println("connect");

       while((s = in.readLine()) != null && !s.isEmpty())
       {
             switch (s)
             {
                 case "red":
                     //set circle to red.
                     red.setFill(Paint.valueOf("#ff1f1f"));
                     System.out.println("red");
                     break;
                 case "yellow":
                     //set circle to yellow
                     yellow.setFill(Paint.valueOf("#36bc0d"));
                     System.out.println("yellow");
                     break;
                 case "green":
                     //set circle to green
                     green.setFill(Paint.valueOf("#bdff1f"));
                     System.out.println("green");
                     break;
                 default:
                     break;
             }
       }

      return echoSocket;
     }
     catch (UnknownHostException e)
    {
        System.err.println("Don't know about host " + hostName);
        System.exit(1);

    } catch (IOException e)
    {
        System.err.println("Couldn't get I/O for the connection to " +  hostName);
        System.exit(1);
    }
     return null;
 }

The problem is that the client prints out the parameters received from the server, then sleeps for a fixed amount of time, but it does not change the colour of the circles until the very end, and all at the same time.

Does anyone have a solution to this? Would greatly appreciate it.

ssandoy
  • 79
  • 1
  • 7
  • I do not understand the question, but it looks like you want to update the color on a certain circle while your background thread is running. You can do this by directly wrapping your `setColor()` inside [`Platform.runLater()`](https://docs.oracle.com/javase/8/javafx/api/javafx/application/Platform.html#runLater-java.lang.Runnable-) or you can also use a [`Task`](https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html). – ItachiUchiha Mar 02 '16 at 12:19
  • Hi. Yes this is what I am trying to achieve. How would I implement it using Platform.runLater()? – ssandoy Mar 02 '16 at 12:25
  • You should go through [JavaFX - Control and Concurrency](http://stackoverflow.com/questions/24983721/javafx-control-and-concurrency) – ItachiUchiha Mar 02 '16 at 12:28
  • I think some of the problem is that the thread is in the server side, while the JavaFX-application is on the client side. Where would I then use the platform.runLater()? Since it's the fx-application that is supposed to update the colour when it receives some String from the server. Not sure if I'm being clear here, and sorry for that.... – ssandoy Mar 02 '16 at 12:34
  • @ItachiUchiha Don't know if you get notifications if I don't use your username or not.... – ssandoy Mar 02 '16 at 12:46
  • @James_D has already added an answer with an example. If it doesn't make sense please add a comment. – ItachiUchiha Mar 02 '16 at 15:31
  • @ItachiUchiha It worked! Thank you so much for your help, too. – ssandoy Mar 02 '16 at 17:33

1 Answers1

1

Run your clientStart method in a background thread and wrap the calls that change the UI in Platform.runLater(...):

while((s = in.readLine()) != null && !s.isEmpty()) {
    final String line = s ;
    Platform.runLater(() -> {
         switch (line)
         {
             case "red":
                 //set circle to red.
                 red.setFill(Paint.valueOf("#ff1f1f"));
                 System.out.println("red");
                 break;
             case "yellow":
                 //set circle to yellow
                 yellow.setFill(Paint.valueOf("#36bc0d"));
                 System.out.println("yellow");
                 break;
             case "green":
                 //set circle to green
                 green.setFill(Paint.valueOf("#bdff1f"));
                 System.out.println("green");
                 break;
             default:
                 break;
         }
    });
}

Then you need to call clientStart() with something like:

Task<Socket> clientStartTask = new Task<Socket>() {
    @Override
    public Socket call() {
        return clientStart();
    }
});

clientStartTask.setOnSucceeded(e -> {
    Socket socket = clientStartTask.getValue();
    // do whatever you need with socket here...
});

Thread thread = new Thread(clientStartTask);
thread.setDaemon(true);
thread.start();
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Appreciate the suggestion greatly, but not really sure how that last block of code should be implemented... Where would I put that code? I initiate the clientStart()-method when I press a connect-button. Is that where I would put the code? Sorry if I'm being unclear.... – ssandoy Mar 02 '16 at 17:15
  • Where are you currently calling `clientStart()`? Since the code I posted causes that method to be executed, that would seem like the obvious place... So, yes, you should do this in your handler for the connect button. – James_D Mar 02 '16 at 17:23
  • It worked! Oh my god, you just saved my life. Thank you so much! – ssandoy Mar 02 '16 at 17:32