7
\$\begingroup\$

I have written a server-side and the client-side of a chat application. I would like to improve it and I am unsure about my server construct and have absolutely no idea if there is a better way to do it. I haven't found anything on Google, therefor I ask you. The construct I have used is from a tutorial from Oracle I found through Google, but I can't find the link now.

My code works like this:

I have a server waiting for clients to connect to this server. If a client wants to connect to the server, the server assigns a ClientConnector to the client. This ClientConnector is pulling the messages from the client. These messages will be stored in two ArrayLists. One is there, if we shut down the server, that it is saving these messages. The other ArrayList is for my class MessageReader, which just prints the messages to the console of the server. It will later forward them to all other clients.

My "problem"/question:

I don't know exactly why I need it and it took me about 2 days to find out. That it is necessary but in my class MessageReader, it won't print out messages unless there is a Thread.sleep.

Server:

package TwoWayCommunicationVersion04.Server;

import TwoWayCommunicationVersion04.Client.Client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

/******************************************************************************
 *
 * Server
 * clientConnectors - Place to store the clients who are connected to the
 *                    server.
 * clientMessages   - Place to temporally store the messages from the
 *                    clients, automatically emptying the ArrayList, when
 *                    the message is handled by the server
 *
 ****************************************************************************/
public class Server extends Thread {

    private static ServerSocket serverSocket;

    private ArrayList<ClientConector> clientConectors = new ArrayList<>();

    private ArrayList<String> clientMessagesBackup= new ArrayList<>();
    private ArrayList<String> clientMessages = new ArrayList<>();


    /**************************************************************************
     * Server-Constructor
     * @param port the port the server listens to
     **************************************************************************/
    public Server(int port) throws IOException {
            this.serverSocket = new ServerSocket(port);
    }

    /**************************************************************************
     * getClientMessageList
     * @return the destination for the clientMessages
     **************************************************************************/
    public ArrayList<String> getClientMessageList() {
        return clientMessages;
    }

    /**************************************************************************
     * listenSockets
     * @param socket ususally serverSocket.accept()
     *
     * task -------- accepting the new Client
     **************************************************************************/
    private void listenSockets(Socket socket) {
        System.out.println("GOT CLIENT");
        ClientConector clientConector = new ClientConector(socket, clientMessages, clientMessagesBackup);
        clientConectors.add(clientConector);
        clientConector.start();
    }

    /**************************************************************************
     * closeServer
     * Saving the collected data to some files and then closing the sockets
     **************************************************************************/
    private void closeServer() throws IOException {
        serverSocket.close();

        /**
         * Going to add that the messages and so on will be safed
         */

    }

    /**************************************************************************
     * startServer
     * If there are some files that the server can use then this mehtod will
     * analyze them first and then use them. These files contain e.g. the
     * Client-ID's of the Clients who connected to the server.
     **************************************************************************/
    private void startServer() {

    }

    @Override
    public void run() {

        try {

            System.out.println("STARTED SERVER");
            while (true) {
                listenSockets(serverSocket.accept());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ClientConnector:

package TwoWayCommunicationVersion04.Server;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;

/**
 *
 *
 */
public class ClientConector extends Thread {

    private static Socket socket;

    private ArrayList<String> clientMessages;
    private ArrayList<String> clientMessagesBackup;

    private static BufferedReader bufferedReader;
    private static BufferedWriter bufferedWriter;



    public ClientConector(Socket socket, ArrayList<String> clientMessages, ArrayList<String> clientMessagesBackup){
        this.socket = socket;
        initStreams();
        setArrayLists(clientMessages, clientMessagesBackup);
    }

    private void setArrayLists(ArrayList<String> clientMessages, ArrayList<String> clientMessagesBackup){
        this.clientMessages = clientMessages;
        this.clientMessagesBackup = clientMessagesBackup;
    }

    private void initStreams() {
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        } catch (IOException e) {
            System.err.println("FAILED TO INITIALIZE STREAMS");
        }
    }

    private void closeClient() {
        try {
            bufferedReader.close();
            bufferedWriter.close();
            socket.close();
        } catch (IOException e) {
            System.err.println("FAILED TO CLOSE SOCKETS AND/OR STREAMS");
        }
    }

    private void pullMessages() throws IOException {
        String message = bufferedReader.readLine();
        if(message != null) {
            System.out.println("GOT MESSAGE" +
                    "\n\t\tARRAYLIST-SIZE: " + clientMessages.size() +
                    "\n\t\tARRAYLIST-SIZE (BACKUP) : " + clientMessagesBackup.size());
            clientMessages.add(message);
            clientMessagesBackup.add(message);
        }
    }

    @Override
    public void run() {
    System.out.println("STARTED DEALING WITH CLIENT");
        try {
            while(true){
                pullMessages();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

MessageReader:

package TwoWayCommunicationVersion04.Server;

import java.util.ArrayList;

/**
 * Created by zeljko on 22.12.15.
 */
public class MessageReader extends Thread{

    private ArrayList<String> clientMessages;

    public MessageReader(ArrayList<String> clientMessages){
        this.clientMessages = clientMessages;
    }

    private void readMessage() throws InterruptedException {
        while(!clientMessages.isEmpty()) {
            System.out.println(clientMessages.get(0));
            clientMessages.remove(0);
        }
        //Without this he will not print out the messages
        Thread.sleep(1);
    }

    @Override
    public void run() {
        while(true) {
            try {
                readMessage();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

If you want to take a look on the rest of the stuff, here is my GitHub link to the repository. There is my client, and "older" versions of the server and client.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

System.out may not be available from the additional thread. Unpredictable behavior results from this type of access stdout, network connections, files, hardware resources. To capture the output, instead use a concurrent logger which can receive output from multiple threads into a thread safe collection, retrieving and appending those messages to a file. There are logj4 and others or you can roll your own.

\$\endgroup\$
1
  • \$\begingroup\$ I though, that if I throw all my input in one central "thing", here an ArrayList, it would work without problems. I have also used some System.out so I don't think that it isn't available. \$\endgroup\$
    – Sparkay
    Commented Dec 24, 2015 at 11:44

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.