JavaFX Socket Handling


Question

To try and illustrate the situation:

I've created a SocketManager class. This extends the JavaFX Service and is intended to create new threads (A JavaFX Task which I've extended and called SocketListener) in which to handle specific socket connections.

Within a SocketListener, I have a loop listening for input over the socket (And replying back to the client).

The problem: The loop within SocketListener is blocking (obviously). This means I never reach the "return" inside the call method of Socket Listener (which extends JavaFX Task). I'm curious what pattern to implement so that I may return a value from the SocketListener without having to break my loop which maintains connection with the client. One idea that came to mind was creating a custom event to fire when each message came over the socket, but I want to make sure whatever I implement doesn't just "work" but is optimal.

Here's the code for the two classes I'm referencing:

import javafx.concurrent.Service;
import javafx.concurrent.Task;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * The primary class for listening for and issuing communication over sockets in VDTS
 * applications.
 * WARNING: This class is blocking, and should be run in a separate thread.
 */
public class SocketManager extends Service<SocketMessage> {

    /**
     * The port that SocketManager instance will listen on.
     */
    int listenPort;

    /**
     * The ServerSocket instance used by SocketManager for receiving socket connections.
     */
    ServerSocket server;

    /**
     * Constructs a SocketManager and begins listening on the given port.
     * @param listenPort The port that the SocketManager instance will listen on.
     */
    public SocketManager(int listenPort){
        this.listenPort = listenPort;
        try {
            this.server = new ServerSocket(listenPort);
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
            //TODO
        }
    }

    /**
     * Returns the port currently being listened to by SocketManager instance.
     * @return the port being listened to.
     */
    public int getListenPort() {
        return listenPort;
    }

    /**
     * Sets a new port for the SocketManager instance to listen to. This will destroy
     * the current ServerSocket, ending any communication via the old instance. Use
     * with caution.
     * @param listenPort The new port this SocketManager instance will listen on.
     */
    public void setListenPort(int listenPort) {
        this.listenPort = listenPort;
    }

    @Override
    protected Task createTask() {
        String returnedMessage;
        Socket client;
        SocketListener messenger;
        while(true){
            try {
                client = null;
                client = server.accept();
                messenger = new SocketListener(client);
                new Thread(messenger).start();
            } catch (IOException ex) {
                System.out.println(ex.getMessage());
                //TODO
            }
        }
    }
}

And the other

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * An extension on javafx.concurrency.Task.
 * SocketListener is responsible for retrieving a message (string) from a client over
 * the socket and returning this string (Usually to a SocketManager).
 */
public class SocketListener extends javafx.concurrent.Task<String> {

    /**
     * Client socket which is referenced for persistent communication.
     */
    private final Socket client;

    /**
     * PrintWriter used to write to client.
     */
    private PrintWriter clientOut;

    //TODO This is a temporary property for testing, remove it;
    private int messageCount = 0;

    /**
     * Constructs a SocketListener given a client (Socket)
     * @param client
     */
    public SocketListener(Socket client){
        this.client = client;
        try {
            this.clientOut = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Listens for and returns the value of the message received over socket.
     * @return The message received from the client.
     * @throws Exception
     */
    @Override
    protected String call() throws Exception {
        String nextLine = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        //TODO remove in production should not require highfive
        clientOut.println("Just saying hi.");
        while ((nextLine = in.readLine()) != null) {
            this.messageCount++;
            clientOut.println("You've messaged me " + this.messageCount + " times.");
        }
        return "Socket closed.";
    }
}
1
4
1/23/2014 4:15:34 AM

Accepted Answer

Why you don't try to use other options? The standard library to input and output data via sockets is not very productive (java.net). You can try the Java NIO library, which will offer you much more flexibility in your code.

NIO offers you the ability to initiate connections so you choose whether or not your code will block your connection, and yet you would have to use a loop. Inside the loop you could observe what type of communication was made ​​between two points, and then treat the communication as you wanted. And yet, with a little more creativity you could use executors services on the server side, thus causing other threads to just have to deal with communications, leaving you with a single thread to be responsible to communicate or exchange data.

JavaFX is a type of technology that came to bring great tools to develop rich applications. As it is considered a different type of technology and use another approach, JavaFX (like Swing) brings some tools to keep your code thread-safe, which is precisely Task, Service, and among other things, as the runLater method from Platform.

So all you can do is mix these two technologies, NIO and JavaFX. At the moment you come to do some processing in your application, be it client or server, perform processing on a thread other than the JavaFX Application Thread. When you have to display data in a JavaFX application, simply use the JavaFX Application Thread for this. There is no more simple thing in the world. ;)

If the NIO technology is not being to your liking, you can still try other kinds of technologies that were "better" developed, such as the Apache MINA project and the NETTY framework. These projects have developed more practical solutions for anyone working with NIO, and they can be very useful, because you will not have to reinvent the wheel.

If you still have questions, please ask. Good luck. (:

1
1/23/2014 1:39:28 PM

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon