Java Thread-pooled server: I am only able to either read the request, or successfully send a response, but not both

Viewed 24

I am currently testing a Java server that I am writing, which is not actually going to be an HTTP server, but for now I am trying to output the client request data received from a browser into the console, and then send a response to the browser, which displays the text I sent in the browser window. I'm following a tutorial from jenkov.com but I'm trying to create an abstract ConnectionHandler to handle all requests on a particular server. Here is my code so far:

TestClient.java

public class TestClient {
    public static void main(String[] args) {
        Server messageServer = new Server(80);
        messageServer.start(new ConnectionHandler() {
            @Override
            public void request(InputStream req, OutputStream res) {
                try {
                    // When I comment out the code for reading from req, then the response
                    // successfully sends, but if I try to read the request and output it to
                    // to the console, it hangs forever
                    BufferedReader reader = new BufferedReader(new InputStreamReader(req));

                    String line;
                    while ((line = reader.readLine()) != null)
                        System.out.println(line);

                    reader.close();
                    long time = System.currentTimeMillis();
                    res.write(("HTTP/1.1 200 OK\n\nWorkerRunnable: " +
                            "Multithreaded Server" + " - " +
                            time +
                            "").getBytes());
                    res.close();
                    System.out.println("Request processed: " + time);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Server.java:

public class Server implements Runnable {
    private int port;
    private Thread runningThread = null;
    private ServerSocket ss = null;
    private boolean running = false;
    private ExecutorService threadPool = Executors.newFixedThreadPool(100);
    private ConnectionHandler handler;

    public Server(int port) {
        this.port = port;
    }

    public void start(ConnectionHandler handler) {
        this.handler = handler;
        this.running = true;
        new Thread(this).start();
    }

    public synchronized boolean isRunning() {
        return running;
    }

    public synchronized void stop() {
        this.running = false;
        try {
            this.ss.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        synchronized (this) {
            this.runningThread = Thread.currentThread();
        }
        try {
            this.ss = new ServerSocket(this.port);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Listening on port " + this.port + "...");

        while (this.isRunning()) {
            Socket client;
            try {
                client = this.ss.accept();
                System.out.println("Incoming connection from " + client.getRemoteSocketAddress());
            } catch (IOException e) {
                if (!this.isRunning()) {
                    System.out.println("The server stopped.");
                    return;
                }
                e.printStackTrace();
                continue;
            }
            this.threadPool.execute(new ClientHandler(client, this.handler));
        }
        this.threadPool.shutdown();
    }
}

ClientHandler.java:

public class ClientHandler implements Runnable {
    private Socket sock = null;
    private String serverText = "Multithreaded Server";
    private ConnectionHandler handler;

    public ClientHandler(Socket sock, ConnectionHandler handler) {
        this.sock = sock;
        this.handler = handler;
    }

    public void run() {
        try {
            this.handler.request(this.sock.getInputStream(), this.sock.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ConnectionHandler.java:

public abstract class ConnectionHandler {
    public abstract void request(InputStream req, OutputStream res);
}

Output in console with the request reading code (but browser hangs and page doesn't load):

Listening on port 80...
Incoming connection from /0:0:0:0:0:0:0:1:59404
GET / HTTP/1.1
Host: localhost
User-Agent: ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ...
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: ...
Upgrade-Insecure-Requests: 1

I guess I want to know if I am doing this right. I really would like to be able to have a single connection handler that works correctly on multiple threads that can be defined inside the TestClient. I'm not sure if I'm supposed to synchronize the ConnectionHandler inside Server.java because I only have one handler that will be access by each thread handling a connection. Please help me figure out what I am doing wrong, I'm kind of new to Java threads...thanks.

1 Answers

I seem to have found the answer to my question here. It appears that when reading a line from the req stream, you have to write something like:

while ((line = reader.readLine()) != null && !line.equals(""))
    System.out.println(line);

This seems to have solved my issue!

Related