0

Hey all I am creating a chat program that can handle multipule clients to one server. Everything works just as it should on the chat side. I can have 3 clients open and talking to each other and it all works fine. However, I am also wanting to place a small web page when someone uses that URL:PORT into their browser and just let them know this is a private server please move on type of thing.

For my client code I have this:

import java.io.*;
import java.net.Socket;
import java.net.SocketException;

public class MainClient {
    private Socket clientSocket;
    public PrintWriter out;
    public BufferedReader userInput;
    private ClientReceiver clientReceiver;
    private static final int SERVER_PORT = 6427;
    private String line;

    public static void main(String[] args) {
        MainClient uiSender = new MainClient();
        uiSender.go();
    }

    private void go() {
        try {
            clientSocket = new Socket("localhost", SERVER_PORT);
            out = new PrintWriter(clientSocket.getOutputStream(), true);
            clientReceiver = new ClientReceiver(this, clientSocket);
            userInput = new BufferedReader(new InputStreamReader(System.in));

            clientReceiver.start();

            while (true) {
                line = userInput.readLine();

                if (!clientSocket.isClosed()) {

                    if (line != null) {
                        out.println(line);

                        if (line.equals("END")) {
                            clientReceiver.interrupt();
                            break;
                        }
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }

        } catch (SocketException e) {
            System.out.println("Main thread socketexception");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();

                if (clientSocket != null) {
                    clientSocket.close();
                }
            } catch (IOException e) {
                System.err.println("Failed to close.");
            }
        }

        System.out.println("Main thread ended");
    }

    public class ClientReceiver extends Thread {
        private Socket clientSocket;
        private MainClient mainHandle;
        private BufferedReader socketIn;
        private String line;

        public ClientReceiver(MainClient mainHandle, Socket clientSocket) {
            this.mainHandle = mainHandle;
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {
            try {
                socketIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

                while (true) {
                    line = socketIn.readLine();

                    if (line != null) {
                        if (line.equals("END")) {
                            System.out.println("Server sent END message.");
                            clientSocket.shutdownOutput();
                            System.out.println("Shutted down socket output");
                        } else if (line.equals("CLOSE_SOCKET_INPUT")) {
                            clientSocket.shutdownInput();
                            System.out.println("Shutted down socket input");
                            break;
                        } else
                            System.out.println(line);

                    } else {
                        System.out.println("Server sent null. Exiting...");
                        break;
                    }
                }
            } catch (SocketException e) {
                System.out.println("Caught SocketException");
            } catch (IOException e) {
                System.out.println("Caught IOException");
                e.printStackTrace();
            } finally {
                try {
                    clientSocket.close();
                    System.out.println("Closed client socket");
                } catch (IOException e) {
                    System.err.println("BufferedReader or socket failed to close.");
                }

                System.out.println("ClientReceiver thread ended.");
                System.out.println("Server has shut down. Press any key to quit.");
            }

        }
    }
}

And the server code is:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class MainServer {
    private MessageQueue messageQueue;
    private ConnectionManager connectionManager;
    private BufferedReader inputStream;
    private MessageRetriever messageRetriever;
    private int _PORT = 6427;
    private Socket socket;
    private BufferedReader in;
    private PrintWriter out;
    private ServerSocket serverSocket;

    public static void main(String[] args) {
        MainServer uiThread = new MainServer();
        uiThread.go();
        System.out.println("Main thread ended");
    }

    private void go() {
        System.out.println("multichat server");
        messageQueue = new MessageQueue();
        connectionManager = new ConnectionManager(messageQueue);
        messageRetriever = new MessageRetriever(messageQueue);
        connectionManager.start();
        messageRetriever.start();
        inputStream = new BufferedReader(new InputStreamReader(System.in));

        String line;

        do {
            try {
                line = inputStream.readLine();
                messageQueue.addToSendMessage(line);

                if (line.equals("END")) {
                    connectionManager.stopListening();
                    messageRetriever.interrupt();
                    break;
                }
            } catch (IOException e) {
                return;
            }
        } while (line != null);
    }

    public class MessageQueue {
        private ArrayList<String> toSendList = new ArrayList<String>();
        private LinkedList<String> receivedMessagesList = new LinkedList<String>();
        private LinkedList<ClientInfo> clientList;
        private boolean shutdownTriggered;

        public MessageQueue() {
            clientList = new LinkedList<ClientInfo>();
        }

        synchronized public void addToSendMessage(String message) {
            toSendList.add(message);
            notifyAll();
        }

        synchronized public List<String> retrieveToSendMessages(int index) throws InterruptedException {
            while (toSendList.size() == index) {
                if (!shutdownTriggered)
                    wait();
                else
                    throw new InterruptedException();
            }

            if (index < toSendList.size()) {
                return toSendList.subList(index, toSendList.size());
            } else {
                System.err.println("Index is " + index + ". Send List size is " + toSendList.size());
                return null;
            }
        }

        synchronized public String pollReceivedMessage() throws InterruptedException {
            while (receivedMessagesList.isEmpty()) {
                if (!shutdownTriggered)
                    wait();
                else
                    throw new InterruptedException();
            }

            return receivedMessagesList.poll();
        }

        synchronized public void addReceivedMessage(String message) {
            receivedMessagesList.push(message);
            notifyAll();
        }

        public void addClient(ClientInfo info) {
            clientList.add(info);
        }

        synchronized public LinkedList<ClientInfo> getClientList() {
            return clientList;
        }

        synchronized public void changeClientInfoStatus(int port, String newStatus) {
            for (ClientInfo info : clientList) {
                if (info.getPort() == port) {
                    info.setStatus(newStatus);
                    break;
                }
            }

            notifyAll();
        }

        public void closeClientCommunicatorInputStreams() {
            for (ClientInfo info : clientList) {
                info.getCommunicator().closeInputStream();
            }
        }

        private boolean allClientInputStreamClosed() {
            for (ClientInfo info : clientList) {
                if (!info.getStatus().equals(ClientInfo.STATE_CLOSED_INPUTSTREAM))
                    return false;
            }

            return true;
        }

        private boolean allClientOutputStreamClosed() {
            for (ClientInfo info : clientList) {
                if (!info.getStatus().equals(ClientInfo.STATE_CLOSED_OUTPUTSTREAM))
                    return false;
            }

            return true;
        }

        synchronized public void waitForAllClientInputStreamClose() {
            while (!allClientInputStreamClosed()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            notifyAll();
        }

        synchronized public void waitForAllClientOutputStreamClose() {
            while (!allClientOutputStreamClosed()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            notifyAll();
        }

        synchronized public void shutdown() {
            shutdownTriggered = true;
            notifyAll();
        }
    }

    public class ClientInfo {
        private ClientCommunicator communicator;
        private String status;
        public static final String STATE_CLOSED_INPUTSTREAM = "state_closed_inputstream";
        public static final String STATE_CLOSED_OUTPUTSTREAM = "state_closed_outputstream";

        public ClientInfo(ClientCommunicator communicator) {
            this.communicator = communicator;
            _PORT = communicator.getSocket().getPort();
        }

        synchronized public void setStatus(String status) {
            this.status = status;
            notifyAll();
        }

        public int getPort() {
            return _PORT;
        }

        synchronized public String getStatus() {
            return status;
        }

        public ClientCommunicator getCommunicator() {
            return communicator;
        }
    }

    public class ClientCommunicator extends Thread {        
        private MessageQueue messageQueue;
        private Sender sender;
        private Socket socket;

        public ClientCommunicator(Socket socket, MessageQueue messageQueue) {
            System.out.println("Connected to new client at port " + socket.getPort() + ". Local port is " + socket.getLocalPort());
            this.messageQueue = messageQueue;
            this.socket = socket;

            try {
                out = new PrintWriter(socket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            sender = new Sender();
            sender.start();

            try {
                while (true) {
                    String receivedMessage = in.readLine(); //GET / HTTP/1.1
                    if (receivedMessage != null)
                        messageQueue.addReceivedMessage(receivedMessage);
                    else
                        break;
                }
            } catch (SocketException e) {
                // Typically this means that the socket has been closed
            } catch (IOException e) {
                System.err.println(e.getMessage());
            }

            System.out.println("ClientCommunicator thread ended.");
        }

        public void closeInputStream() {
            if (!socket.isInputShutdown()) {
                try {
                    socket.shutdownInput();
                    messageQueue.changeClientInfoStatus(socket.getPort(), ClientInfo.STATE_CLOSED_INPUTSTREAM);
                } catch (IOException e) {
                    System.err.println("Failed to close socket input stream.");
                    e.printStackTrace();
                }
            }
        }

        public void closeOutputStream() {
            if (!socket.isOutputShutdown()) {
                try {
                    socket.shutdownOutput();
                } catch (IOException e) {
                    System.err.println("Failed to close socket output stream.");
                    e.printStackTrace();
                }
            }
        }

        public Socket getSocket() {
            return socket;
        }

        private class Sender extends Thread {
            private int toSendMessageIndex;

            @Override
            public void run() {
                while (true) {
                    try {
                        List<String> messages = messageQueue.retrieveToSendMessages(toSendMessageIndex);

                        if (messages != null) {
                            boolean exit = false;

                            for (String message : messages) {
                                out.println(message); //GET / HTTP/1.1
                                toSendMessageIndex++;

                                if (message.equals("CLOSE_SOCKET_INPUT")) {
                                    closeOutputStream();
                                    messageQueue.changeClientInfoStatus(socket.getPort(), ClientInfo.STATE_CLOSED_OUTPUTSTREAM);
                                    exit = true;
                                    break;
                                } else if (message.equals("GET / HTTP/1.1")) {
                                    System.out.println("GET");
                                    loadWP();
                                    //loadWEBPage();
                                }
                            }

                            if (exit)
                                break;
                        } else {
                            System.err.println("Null messages!");
                            break;
                        }
                    } catch (InterruptedException e) {
                        break;
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

                System.out.println("ClientCommunicator.Sender thread stopped.");
            }
        }
    }

    public class ConnectionManager extends Thread {
        private final int PORT = _PORT;
        private MessageQueue messageQueue;

        public ConnectionManager(MessageQueue messageQueue) {
            this.messageQueue = messageQueue;
        }

        @Override
        public void run() {
            try {
                serverSocket = new ServerSocket(PORT, 0, InetAddress.getByName("localhost"));
                System.out.println("Server listening on port " + PORT);

                while (true) {
                    socket = serverSocket.accept();
                    ClientCommunicator communicator = new ClientCommunicator(socket, messageQueue);
                    ClientInfo clientInfo = new ClientInfo(communicator);
                    communicator.start();
                    messageQueue.addClient(clientInfo);
                }
            } catch (SocketException e) {
                // SocketException is thrown when serverSocket is closed. This
                // is normal when we shut down the server.
            } catch (IOException e) {
                System.err.println(e.getMessage());
            } finally {
                System.out.println("ConnectionManager thread ended.");
            }
        }

        public void stopListening() {
            try {
                messageQueue.closeClientCommunicatorInputStreams();
                messageQueue.waitForAllClientInputStreamClose();
                messageQueue.addToSendMessage("CLOSE_SOCKET_INPUT");
                messageQueue.waitForAllClientOutputStreamClose();

                System.out.println("Closing client sockets.");

                for (ClientInfo info : messageQueue.getClientList()) {
                    Socket socket = info.getCommunicator().getSocket();

                    if (!socket.isClosed()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            System.err.println("Failed to close client socket.");
                        }
                    } else {
                        System.err.println("Client socket is already closed.");
                    }
                }

                if (serverSocket != null) {
                    serverSocket.close();
                    System.out.println("Closed server socket.");
                }

                messageQueue.shutdown();
            } catch (IOException e) {
                System.err.println("Failed to close socket");
            }
        }
    }

    public class MessageRetriever extends Thread {
        private MessageQueue messageQueue;

        public MessageRetriever(MessageQueue messageQueue) {
            this.messageQueue = messageQueue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    String receivedMessage = messageQueue.pollReceivedMessage();

                    if (receivedMessage == null) {
                        System.err.println("Received null message. Terminating.");
                        break;
                    } else {
                        System.out.println(receivedMessage);
                        messageQueue.addToSendMessage(receivedMessage);
                    }
                } catch (InterruptedException e) {
                    break;
                }
            }

            System.out.println("MessageRetriever ended.");
        }
    }

    public void loadWEBPage() {
        final String newLine = "\r\n";

        try {
            while (true) {
                try {
                    String request = in.readLine();

                    if (request == null)
                        continue;

                    while (true) {
                        String ignore = in.readLine();
                        if (ignore == null || ignore.length() == 0)
                            break;
                    }

                    if (!request.startsWith("GET ")
                            || !(request.endsWith(" HTTP/1.0") || request.endsWith(" HTTP/1.1"))) {
                        out.print("HTTP/1.0 400 Bad Request" + newLine + newLine);
                    } else {
                        String response = "Hello, World!";

                        out.print("HTTP/1.0 200 OK" + newLine + "Content-Type: text/plain" + newLine + "Date: "
                                + new Date() + newLine + "Content-length: " + response.length() + newLine + newLine
                                + response);
                    }

                    out.close();
                } catch (Throwable tri) {
                    System.err.println("Error handling request: " + tri);
                }
            }
        } catch (Throwable tr) {
            System.err.println("Could not start server: " + tr);
        }
    }
    }

Once I start up the socket server I test the chat and again it works just fine back and forth. Then I test out the website http:\localhost:6427 and this is the output I get from the Eclipse console once I do that:

multichat server

Server listening on port 6427

Connected to new client at port 58833. Local port is 6427

Upgrade-Insecure-Requests: 1

Accept-Language: en-US,en;q=0.9

Accept-Encoding: gzip, deflate, br

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/a png,/;q=0.8,application/signed-exchange;v=b3

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36 Connection: keep-alive Host: localhost:6427 GET / HTTP/1.1 Exception in thread "Thread-8" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1250) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1110) at java.util.AbstractList.listIterator(AbstractList.java:310) at java.util.ArrayList$SubList.iterator(ArrayList.java:1106) at MainServer$ClientCommunicator$Sender.run(MainServer.java:292)

Connected to new client at port 58834. Local port is 6427

GET

ClientCommunicator thread ended.

I had it working at one point then I tried something else and once that didn't work I tried to remember what I did with the other script and alas I forgot....

Anyone care to point out what I am missing to get this working again?

1 Answer 1

0

In your code you iterate through the messages-List but while this happens something else adds another element to the list or removes one so you get ConcurrentModificationException.

For example: This will not work:

public static void main(String[] args)
{
   List<Object> objectList = new ArrayList(Arrays.asList(new Object[] { "1", "2", "3"}));
   int index = 0;
   for (Object object : objectList)
   {
      System.out.println(object.toString());
      objectList.remove(index);
      index++;
   }
}

But if you use an Iterator this works:

public static void main(String[] args)
{
   List<Object> objectList = new ArrayList(Arrays.asList(new Object[] { "1", "2", "3"}));
   Iterator<Object> iterator = objectList.iterator();
   while (iterator.hasNext())
   {
      Object object = iterator.next();
      System.out.println(object.toString());
      iterator.remove();
   }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the reply. Could you by chance use the code I have to do what you did above. Can’t really imagine where that is in my own code.
Take a look at the method .retrieveToSendMessages() from your MessageQueue-Class. I guess this method returns the reference to a List but you would need a new List so the MessageQueue can receive new ToSendMessages while you are still working with the "older" ones.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.