0
votes

I wish to efficiently implement the server side of a long polling system. A client connects to my server and sends a request. The server sends a response after a long (but variable) delay, let's say 10 minutes.

But if the client goes away and terminates the socket, I want the server to detect this condition and release its own socket without waiting for the full length of the timeout. This allows the handler thread to terminate or do other things (because each client is handled by a separate thread).

My question: Is this kind of logic possible to implement? The server pseudocode I'm thinking of is like this:

private Socket socket;  // Received from constructor

public void run() {
    readRequest(socket.getInputStream());
    sleepUnlessClosed(socket, 600000);  // Wait either 10 minutes or socket closure
    if (!socket.isClosed())
        writeResponse(socket.getOutputStream());
    socket.close();
}

As far as potential solutions go: I am willing to consider using an external thread to poll if the socket is closed. I'm reluctant to look at NIO, Channels, and Selector because they involve a different paradigm.

1
You'll need one thread for a read on the socket so that you sense the close, and a timer. Actions on both events (close, expiry) are straightforward. - laune
Why the delay between request and response? - user207421
@EJP Because the server is sending user-specific updates back to the client, which can be quite infrequent compared to the polling interval. I forgot to mention that the delay can be shortened if a new message arrives before the full timeout. - Nayuki
have you thought about making the whole system asynchronous? I mean, since your response is not real time wrt the system and you should avoid long busy waiting on resources like sockets you may want to generate a UID for a consequent request upon which, if the response is ready you send it, otherwise the client will try again later.. - LMG
@LMG I'm not busy-waiting. I am either sleeping the thread or blocking on I/O. - Nayuki

1 Answers

0
votes

So what you want is something like the following

public class MyServerSocket {
    int state = 0;
    public static void main(String[] args) {
        new MyServerSocket();
    }

    public MyServerSocket() {
        try {
            init();
        } catch (IOException ex) {
            Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void init() throws IOException {
        int portNumber = 4444;

        try (
                ServerSocket serverSocket = new ServerSocket(portNumber);
                Socket clientSocket = serverSocket.accept();
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));) {
            MyReadThread mrt = new MyReadThread(this, in);
            Thread t = new Thread(mrt);
            t.start();
            int maxAwake = 10;
            int i = 0;
            while(state == 0 && !(i>maxAwake)){
                Thread.sleep(1000);
                i++;
                Logger.getLogger(MyServerSocket.class.getName()).info("awake and asleep");
            }
            Logger.getLogger(MyServerSocket.class.getName()).info("awake counter: " + i);
            out.println("Good bye");
            out.close();
            in.close();
            clientSocket.close();
            serverSocket.close();
        } catch (InterruptedException ex) {
            Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

where the Thread is reading the input in the following way

public class MyReadThread implements Runnable {

    BufferedReader in = null;
    MyServerSocket caller = null;

    public MyReadThread(MyServerSocket caller, BufferedReader in) {
        this.in = in;
        this.caller = caller;
    }

    @Override
    public void run() {
        if (in == null || caller == null) {
            throw new RuntimeException("Input stream or caller server socket is null");
        }
        String inputLine = "";
        boolean quit = false;
        while (!quit) {
            try {
                inputLine = in.readLine();
            } catch (IOException e) {
                if (e instanceof SocketException){
                    inputLine = null;
                }
                else{
                   Logger.getLogger(MyServerSocket.class.getName()).log(Level.SEVERE, null, e);
                }
            }
            if (inputLine == null) {
                Logger.getLogger(MyServerSocket.class.getName()).warning("Client suddenly disconnected");
                quit = true;
                caller.state = 1;
            }
            Logger.getLogger(MyServerSocket.class.getName()).info("input line over connection is: " + inputLine);
            if ("quit".equals(inputLine)) {
                quit = true;
                caller.state = 2;
            }
        }

    }
}

This is just a sketch, of course. Notice that you are not using NIO, Channels nor Selectors…

A similar question was asked here Checking for a client disconnect on a Java TCP server - output only

But in that case no thread were used. In this case your main socket is actually sleeping until maxAwake while the thread is reading what you are sending...