Title: Solution to Chat Server Exercise
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

Solution to the Chat Server Exercise

ChatClient.java


import java.io.*;
import java.net.*;

public class ChatClient extends Thread{
  BufferedReader input;
  boolean socketAlive = true;
  ChatClient()
  {
  }

  public void run()
  {
     String dataFromSocket;
     try{
     // Read from Socket and write to Screen
        while ((dataFromSocket = input.readLine()) != null) // Read from Socket
        {
            System.out.println(dataFromSocket);
        }
     }
     catch (IOException e)
     {
        System.out.println("Err: " +e);
     }
     // It's easier for the socket reader to detect that the socket
     // is gone.  We need to set a flag so that the socket writer
     // will know that it's time to give up.

     socketAlive = false;
     System.out.println("ChatClient Thread terminating (read from socket ---> Write to screen)");

  }
  void main_loop(String host, int port)
  {
        Socket ChatSocket = null;
        PrintWriter out = null;
        BufferedReader keyboard =null;

        try
        {
            ChatSocket = new Socket(host, port);

            // Create in/out classes associated with the Open Socket

            out = new PrintWriter(ChatSocket.getOutputStream(), true);

            input = new BufferedReader(
                        new InputStreamReader(
                            ChatSocket.getInputStream()));

            // Create Input class for the Keyboard

            keyboard = new BufferedReader(
                            new InputStreamReader(System.in));

            System.out.println("Enter Name:");
            String name= keyboard.readLine();
            name = name +": ";

            String userInput, dataFromSocket;

            // Start up a Thread to read from the socket and write
            //   the contents to the screen

            this.start();

            // Read from Keyboard, and write contents back to socket
            //   with name prepended to the front of the message.

            while ((userInput = keyboard.readLine()) != null) // Read from Socket
            {
                if ("quit".equals(userInput)) break;
                if (!socketAlive) break;
                out.println(name + userInput); // Write to socket
            }
        }
        catch (UnknownHostException e)
        {
            System.err.println("Cant find host: "+ e);
            System.exit(1);
        }
        catch (IOException e)
        {
            System.err.println("Couldn't get I/O: " +e);
            System.exit(1);
        }
        finally
        {
            System.out.println("Termination main ChatClient Thread (Keyboard --> Socket)");
            try{
                if (out != null)out.close(); // Close output stream side of the socket
                if (input != null)input.close();  // Close input stream side of socket
                if (keyboard != null)keyboard.close(); // Close keyboard
                if (ChatSocket != null)ChatSocket.close();  // Close the socket
            }
            catch (IOException e){}
        }


  }

  public static void main(String[] args) throws IOException
  {
        ChatClient myChat = new ChatClient();

        if (args.length < 2)
        {
            System.out.println("Usage java ChatClient Host portNum");
            return;
        }
        myChat.main_loop( args[0], Integer.parseInt(args[1]));

  }// end of main
}// end of ChatClient class



ChatServer.java


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

public class ChatServer extends Thread{
    LinkedList chats = new LinkedList();
    int portNum = 4444;
    boolean listening = true;
    ServerSocket serverSocket = null;


    // Utility method to take string received from one Chat conversation and
    //    put it into the Message Queue of all Chat Conversations

    synchronized void putMsgs(String str)
    {
        for (int i=0; i < chats.size(); i++)
        {
            Chat c = (Chat)chats.get(i);
            c.put(str);
        }
    }

    synchronized void removeMe(int index)
    {
        for (int i=0; i < chats.size(); i++)
        {
            Chat c = (Chat)chats.get(i);
            if (index == c.myIndex)
            {
                c.stopChat();
                chats.remove(c);
                break;
            }
        }
        System.out.println("New chats size:" + chats.size());
    }

    public void run()
    {
        InetAddress iaddr=null;
        Socket nextSock;
        int nThreadCount=0;

        try {
                serverSocket = new ServerSocket(portNum);
                iaddr = InetAddress.getLocalHost();
                System.out.println("Host:" + iaddr.getHostName()+
                    " ipaddr=" + iaddr.getHostAddress()+
                    " portnum="+ portNum);

                while (listening)
                {
                    nextSock = serverSocket.accept();
                    System.out.println(nThreadCount +" Another Thread Created");

                    // Create a thread to process incoming connection
                    Chat chatChild = new Chat(nextSock, this, nThreadCount++);
                    chats.add(chatChild);
                    chatChild.start();
                }

                serverSocket.close();

            }
        catch (IOException e)
            {
                System.err.println("Socket Err:" + e);
            }

    }

    public static void main(String[] args) throws IOException {
     ChatServer cserver = new ChatServer();
     if (args.length > 0)
     {
         cserver.portNum = Integer.parseInt(args[0]);
     }

     // Start the Main Server Task --

     cserver.start();

     InputStreamReader ir = new InputStreamReader(System.in);
     BufferedReader br = new BufferedReader(ir);

     String str;
     do
     {
        str = br.readLine();
     }while (!"quit".equals(str));

     cserver.listening = false;
     try
     {
         cserver.serverSocket.close();
     } catch (IOException e){}

// Go in the inverse order because we are removing entries from
// the linkedlist.  This then affects subsequent iterations.
// This needs a picture to explain.

     for (int i=cserver.chats.size()-1; i >= 0; i--)
     {
        Chat c = (Chat)cserver.chats.get(i);
        c.stopChat();
     }

    }
}



Chat.java


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

public class Chat extends Thread {
    private Socket socket = null;
    int myIndex;
    boolean continueFlag = true;
    PrintWriter out=null;
    ChatServer mom;
    LinkedList msgs= new LinkedList();

    synchronized void stopChat()
    {
        continueFlag = false;
        notify(); // wake up thread waiting for a message
        try{
            socket.close();
            }catch (IOException e){}
    }

    // Add string to list for this Chat Conversation
    synchronized void put(String str)
    {
        msgs.add(str);
        notify(); // wake up Thread waiting for this message
    }

    // Either return a String from the list or wait for
    // a new message to enter the queue for this Conversation

    synchronized String get()
    {
        String retval;
        while (continueFlag)
        {
            if (msgs.size() > 0)
            {
                retval = (String)msgs.removeFirst();
                return retval;
            }
            else
            {
                try
                {
                    wait();
                }catch (InterruptedException e){}
            }
        }
        return null;
    }

    public Chat(Socket s, ChatServer m, int me)
    {
       socket = s;
       myIndex= me;
       mom =m; // Mother ChatServer Task
    }

    public void run()
    {
        // Start up a thread to read from the socket and then
        //   put it into all of the Message Queues for all
        //   Chat conversations.

        ChatRead cr = new ChatRead(socket, mom, myIndex);
        cr.start();

       try {
            out = new PrintWriter(socket.getOutputStream(), true);

            String inputLine;

            // Loop to pull messages out of the Queue and then
            // write them to the Socket.

            while ((inputLine = get()) != null) // Pull message from Queue
            {
               out.println(inputLine);
               if (out.checkError()) break;
            }
            socket.close();
           }
       catch (IOException e)
           {
             System.out.println("Err: " + e);
           }
       finally
           {
             System.out.println("Terminating Chat Thread: " + myIndex);
             try
             { // I'm annoyed that I need try ... catch to keep the compiler happy here
                out.close();
                socket.close();

             } catch (Exception e){
                System.out.println("Exception closing sockets :"+e);
             }
           }
    }// end of run routine
}



ChatRead.java


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

public class ChatRead extends Thread {
    ChatServer mom;
    Socket sock;
    BufferedReader in = null;
    int myIndex;

    ChatRead(Socket s, ChatServer m, int index)
    {
        sock = s;
        mom =m;
        myIndex = index;
    }



    public void run()
    {
       // Read from socket and put the string read into all message queues for
       // all converstations.

       try {

            in = new BufferedReader( new InputStreamReader(
                           sock.getInputStream()));

            String inputLine;

            while ((inputLine = in.readLine()) != null) // Read from socket
            {
               mom.putMsgs(inputLine);
            }
           }
       catch (IOException e)
           {
             System.out.println(" Err: " + e);
           }
       finally
           {
             try
             { // I'm annoyed that I need try ... catch to keep the compiler happy here

                in.close();
                sock.close();
                System.out.println(myIndex + ": ChatRead Thread Exitting");
                mom.removeMe(myIndex);
             } catch (Exception e){}
           }
    }// end of run routine
}