본문 바로가기

00./05. JAVA

JAVA Applet Client for Chat Server 자바 채팅 서버 + Applet 클라이언트 소스



개발환경 : Windows 7

개발언어 : JAVA

개발도구 : Eclipse

사용도구 : APM



소스 파일 다운


Chat Server.zip



Chat Server

- Applet Clinet

src

ChatApplet.html

ChatApplet.java

ChatClient.java


- Chat Server

src

NakovChatServer.java



NakovChartServer.java


/** 

 * Nakov Chat Server 

 * (c) Svetlin Nakov, 2002 

 * http://www.nakov.com 

 * 

 * This program is an example from the book "Internet 

 * programming with Java" by Svetlin Nakov. It is freeware. 

 * For more information: http://www.nakov.com/books/inetjava/ 

 * 

 * Nakov Chat Server is multithreaded chat server. It accepts 

 * multiple clients simultaneously and serves them. Clients are 

 * able to send messages to the server. When some client sends 

 * a message to the server, the message is dispatched to all 

 * the clients connected to the server. 

 * 

 * The server consists of two components - "server core" and 

 * "client handlers". 

 * 

 * The "server core" consists of two threads: 

 *   - NakovChatServer - accepts client connections, creates 

 * client threads to handle them and starts these threads 

 *   - ServerDispatcher - waits for messages and when some 

 * message arrive sends it to all the clients connected to 

 * the server 

 * 

 * The "client handlers" consist of two threads: 

 *   - ClientListener - listens for message arrivals from the 

 * socket and forwards them to the ServerDispatcher thread 

 *   - ClientSender - sends messages to the client 

 * 

 * For each accepted client, a ClientListener and ClientSender 

 * threads are created and started. A Client object is also 

 * created to maintain the information about the client and is 

 * added to the ServerDispatcher's clients list. When some 

 * client is disconnected, is it removed from the clients list 

 * and both its ClientListener and ClientSender threads are 

 * interrupted. 

 */ 

 

import java.net.*; 

import java.io.*; 

import java.util.Vector; 

 

/** 

 * NakovChatServer class is the entry point for the server. 

 * It opens a server socket, starts the dispatcher thread and 

 * infinitely accepts client connections, creates threads for 

 * handling them and starts these threads. 

 */ 

public class NakovChatServer { 

    public static final int LISTENING_PORT = 2002; 

    public static String KEEP_ALIVE_MESSAGE = "!keep-alive"; 

    public static int CLIENT_READ_TIMEOUT = 5*60*1000; 

    private static ServerSocket mServerSocket; 

 

    private static ServerDispatcher mServerDispatcher; 

 

    public static void main(String[] args) { 

        // Start listening on the server socket 

        bindServerSocket(); 

 

        // Start the ServerDispatcher thread 

        mServerDispatcher = new ServerDispatcher(); 

        mServerDispatcher.start(); 

 

        // Infinitely accept and handle client connections 

        handleClientConnections(); 

    } 

 

    private static void bindServerSocket() { 

        try { 

            mServerSocket = new ServerSocket(LISTENING_PORT); 

            System.out.println("NakovChatServer started on " + 

                "port " + LISTENING_PORT); 

        } catch (IOException ioe) { 

            System.err.println("Can not start listening on " + 

                "port " + LISTENING_PORT); 

            ioe.printStackTrace(); 

            System.exit(-1); 

        } 

    } 

 

    private static void handleClientConnections() { 

        while (true) { 

            try { 

                Socket socket = mServerSocket.accept(); 

                Client client = new Client(); 

                client.mSocket = socket; 

                ClientListener clientListener = new 

                    ClientListener(client, mServerDispatcher); 

                ClientSender clientSender = 

                    new ClientSender(client, mServerDispatcher); 

                client.mClientListener = clientListener; 

                clientListener.start(); 

                client.mClientSender = clientSender; 

                clientSender.start(); 

                mServerDispatcher.addClient(client); 

            } catch (IOException ioe) { 

                ioe.printStackTrace(); 

            } 

        } 

    } 

 

 

/** 

 * ServerDispatcher class is purposed to listen for messages 

 * received from the clients and to dispatch them to all the 

 * clients connected to the chat server. 

 */ 

class ServerDispatcher extends Thread { 

    private Vector mMessageQueue = new Vector(); 

    private Vector mClients = new Vector(); 

 

    /** 

     * Adds given client to the server's client list. 

     */ 

    public synchronized void addClient(Client aClient) { 

        mClients.add(aClient); 

    } 

 

    /** 

     * Deletes given client from the server's client list if 

     * the client is in the list. 

     */ 

    public synchronized void deleteClient(Client aClient) { 

        int clientIndex = mClients.indexOf(aClient); 

        if (clientIndex != -1) 

            mClients.removeElementAt(clientIndex); 

    } 

 

    /** 

     * Adds given message to the dispatcher's message queue and 

     * notifies this thread to wake up the message queue reader 

     * (getNextMessageFromQueue method). dispatchMessage method 

     * is called by other threads (ClientListener) when a 

     * message is arrived. 

     */ 

    public synchronized void dispatchMessage( 

            Client aClient, String aMessage) { 

        Socket socket = aClient.mSocket; 

        String senderIP = 

            socket.getInetAddress().getHostAddress(); 

        String senderPort = "" + socket.getPort(); 

        aMessage = senderIP + ":" + senderPort + 

            " : " + aMessage; 

        mMessageQueue.add(aMessage); 

        notify(); 

    } 

 

    /** 

     * @return and deletes the next message from the message 

     * queue. If there is no messages in the queue, falls in 

     * sleep until notified by dispatchMessage method. 

     */ 

    private synchronized String getNextMessageFromQueue() 

    throws InterruptedException { 

        while (mMessageQueue.size()==0) 

            wait(); 

        String message = (String) mMessageQueue.get(0); 

        mMessageQueue.removeElementAt(0); 

        return message; 

    } 

 

    /** 

     * Sends given message to all clients in the client list. 

     * Actually the message is added to the client sender 

     * thread's message queue and this client sender thread 

     * is notified to process it. 

     */ 

    private void sendMessageToAllClients( 

            String aMessage) { 

        for (int i=0; i<mClients.size(); i++) { 

            Client client = (Client) mClients.get(i); 

            client.mClientSender.sendMessage(aMessage); 

        } 

    } 

 

    /** 

     * Infinitely reads messages from the queue and dispatches 

     * them to all clients connected to the server. 

     */ 

    public void run() { 

        try { 

            while (true) { 

                String message = getNextMessageFromQueue(); 

                sendMessageToAllClients(message); 

            } 

        } catch (InterruptedException ie) { 

            // Thread interrupted. Stop its execution 

        } 

    } 

 

 

/** 

 * Client class contains information about a client, 

 * connected to the server. 

 */ 

class Client { 

    public Socket mSocket = null; 

    public ClientListener mClientListener = null; 

    public ClientSender mClientSender = null; 

 

 

/** 

 * ClientListener class listens for client messages and 

 * forwards them to ServerDispatcher. 

 */ 

class ClientListener extends Thread { 

    private ServerDispatcher mServerDispatcher; 

    private Client mClient; 

    private BufferedReader mSocketReader; 

 

    public ClientListener(Client aClient, ServerDispatcher 

            aSrvDispatcher) throws IOException { 

        mClient = aClient; 

        mServerDispatcher = aSrvDispatcher; 

        Socket socket = aClient.mSocket; 

        socket.setSoTimeout( 

            NakovChatServer.CLIENT_READ_TIMEOUT); 

        mSocketReader = new BufferedReader( 

            new InputStreamReader(socket.getInputStream()) ); 

    } 

 

    /** 

     * Until interrupted, reads messages from the client 

     * socket, forwards them to the server dispatcher's 

     * queue and notifies the server dispatcher. 

     */ 

    public void run() { 

        try { 

            while (!isInterrupted()) { 

                try { 

                    String message = mSocketReader.readLine(); 

                    if (message == null) 

                        break; 

                    mServerDispatcher.dispatchMessage( 

                        mClient, message); 

                } catch (SocketTimeoutException ste) { 

                    mClient.mClientSender.sendKeepAlive(); 

                } 

            } 

        } catch (IOException ioex) { 

            // Problem reading from socket (broken connection) 

        } 

 

        // Communication is broken. Interrupt both listener and 

        // sender threads 

        mClient.mClientSender.interrupt(); 

        mServerDispatcher.deleteClient(mClient); 

    } 

 

/** 

 * Sends messages to the client. Messages waiting to be sent 

 * are stored in a message queue. When the queue is empty, 

 * ClientSender falls in sleep until a new message is arrived 

 * in the queue. When the queue is not empty, ClientSender 

 * sends the messages from the queue to the client socket. 

 */ 

class ClientSender extends Thread { 

    private Vector mMessageQueue = new Vector(); 

 

    private ServerDispatcher mServerDispatcher; 

    private Client mClient; 

    private PrintWriter mOut; 

 

    public ClientSender(Client aClient, ServerDispatcher 

            aServerDispatcher) throws IOException { 

        mClient = aClient; 

        mServerDispatcher = aServerDispatcher; 

        Socket socket = aClient.mSocket; 

        mOut = new PrintWriter( 

            new OutputStreamWriter(socket.getOutputStream()) ); 

    } 

 

    /** 

     * Adds given message to the message queue and notifies 

     * this thread (actually getNextMessageFromQueue method) 

     * that a message is arrived. sendMessage is always called 

     * by other threads (ServerDispatcher). 

     */ 

    public synchronized void sendMessage(String aMessage) { 

        mMessageQueue.add(aMessage); 

        notify(); 

    } 

 

    /** 

     * Sends a keep-alive message to the client to check if 

     * it is still alive. This method is called when the client 

     * is inactive too long to prevent serving dead clients. 

     */ 

    public void sendKeepAlive() { 

        sendMessage(NakovChatServer.KEEP_ALIVE_MESSAGE); 

    } 

 

    /** 

     * @return and deletes the next message from the message 

     * queue. If the queue is empty, falls in sleep until 

     * notified for message arrival by sendMessage method. 

     */ 

    private synchronized String getNextMessageFromQueue() 

            throws InterruptedException { 

        while (mMessageQueue.size()==0) 

            wait(); 

        String message = (String) mMessageQueue.get(0); 

        mMessageQueue.removeElementAt(0); 

        return message; 

    } 

 

    /** 

     * Sends given message to the client's socket. 

     */ 

    private void sendMessageToClient(String aMessage) { 

        mOut.println(aMessage); 

        mOut.flush(); 

    } 

 

    /** 

     * Until interrupted, reads messages from the message queue 

     * and sends them to the client's socket. 

     */ 

    public void run() { 

        try { 

            while (!isInterrupted()) { 

                String message = getNextMessageFromQueue(); 

                sendMessageToClient(message); 

            } 

        } catch (Exception e) { 

            // Commuication problem 

        } 

 

        // Communication is broken. Interrupt both listener 

        // and sender threads 

        mClient.mClientListener.interrupt(); 

        mServerDispatcher.deleteClient(mClient); 

    } 

}







CharApplet.java


/**

 * Chat applet client for Nakov Chat Server.

 * Author: Nikolay Nedyalkov, 2002

 *

 * ChatApplet class provides applet-based graphical user interface

 * for the clients of NakovChatServer.

 */

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

import java.util.Vector;

import java.util.Enumeration;

 

public class ChatApplet extends Applet implements ActionListener, MouseListener {

     boolean isStandalone = false;

 

     private ChatClient cl           = new ChatClient (this);

     private PopupMenu popupMenu1    = new PopupMenu ();

     private MenuItem menuItem1      = new MenuItem ();

     private MenuItem menuItem2      = new MenuItem ();

     private Vector allUsers         = new Vector ();

     private Vector deniedUsers      = new Vector ();

     private boolean isConnected     = false;

 

     private TextField textField1    = new TextField();

     private Button connectButton    = new Button ();

     private Button disconnectButton = new Button ();

     private Button sendButton       = new Button ();

     private Button clearButton      = new Button ();

     private Button exitButton       = new Button ();

 

     public TextArea textArea1      = new TextArea();

     public List list1              = new List();

 

     public void setConnected(boolean aConnected) {

          isConnected = aConnected;

     }

 

     public boolean getConnected()   {

          return isConnected;

     }

 

     /**

      * Method is called from ChatClient to get reference to ChatApplet.textArea1.

      * @return java.awt.TextArea

      */

     public TextArea getTextArea () {

          return textArea1;

     }

 

     /**

      * Method is called from ChatClient to get reference to ChatApplet.list1.

      * @return java.awt.List

      */

     public List getList () {

          return list1;

     }

 

     /**

      * Method is called from ChatClient to register anUser in allUsers vector

      * and list1 visual control.

      * @param anUser - user to be included.

      */

     public synchronized void addUser (String anUser) {

          if (!allUsers.contains(anUser)) {

               allUsers.addElement (anUser);

               list1.add (anUser);

          }

     }

 

     /**

      * Method is called from ChatClient to append given message to the

      * ChatApplet's TextArea. It also checks whether anUser is in our

      * Ignore list and ignores the message in this case.

      * @param anUser - user that have sent the message

      * @param aText - message to be appened to the applet's TextArea

      */

     public synchronized void addText (String aText, String anUser) {

          if (!deniedUsers.contains(anUser)) {

               textArea1.append (aText + "\n");

          }

     }

 

     public synchronized void addSystemMessage(String aText) {

          textArea1.append(aText + "\n");

     }

 

     /**

      * Applet life cycle initiliazation.

      */

     public void init() {

          try {

               jbInit();

          }

          catch(Exception e) {

               e.printStackTrace();

          }

     }

 

     /**

      * Component initialization.

      * @throws Exception

      */

     private void jbInit() throws Exception {

          this.setLayout (null);

 

          // -- Begin buttons section

          sendButton.setLabel("Send");

          sendButton.setBounds (new Rectangle(316, 245, 68, 23));

          sendButton.addActionListener (this);

          this.add(sendButton);

 

          connectButton.setLabel("connect");

          connectButton.setBounds(new Rectangle(34, 270, 95, 22));

          connectButton.addActionListener(this);

          this.add(connectButton, null);

 

          disconnectButton.addActionListener(this);

          disconnectButton.setBounds(new Rectangle(175, 270, 108, 22));

          disconnectButton.setLabel("disconnect");

          this.add(disconnectButton, null);

 

          clearButton.setLabel("Clear");

          clearButton.addActionListener(this);

          clearButton.setBounds(new Rectangle(316, 270, 68, 23));

          this.add(clearButton, null);

 

          exitButton.setBackground(Color.gray);

          exitButton.setForeground(Color.lightGray);

          exitButton.setLabel("Q");

          exitButton.setBounds(new Rectangle(388, 245, 32, 48));

          exitButton.addActionListener(this);

          this.add(exitButton, null);

          // -- End buttons section

 

          // -- Begin edit controls

          textField1.setBounds(new Rectangle(10, 245, 303, 23));

          textField1.addActionListener(this);

          textField1.setBackground(Color.lightGray);

          this.add(textField1, null);

 

          textArea1.setBounds(new Rectangle(10, 10, 303, 233));

          textArea1.setBackground(Color.lightGray);

          this.add(textArea1, null);

          // -- End edit controls

 

          // -- Begin menus section

          popupMenu1.setLabel("User menu");

          menuItem1.setLabel("Ignore user");

          menuItem1.setActionCommand ("BAN");

          menuItem1.addActionListener (this);

          menuItem2.setLabel("Deignore user");

          menuItem2.setActionCommand ("UNBAN");

          menuItem2.addActionListener (this);

          popupMenu1.add(menuItem1);

          popupMenu1.addSeparator();

          popupMenu1.add(menuItem2);

          // -- End menus section

 

          list1.setBounds(new Rectangle(316, 11, 104, 233));

          list1.add (popupMenu1);

          list1.addActionListener (this);

          list1.addMouseListener (this);

          list1.setBackground(Color.lightGray);

          this.add (popupMenu1);

          this.add(list1, null);

 

          this.setBackground(Color.cyan);

     }

 

     /**

      * Method sends aMessage to server through ChatClient.

      * @param aMessage

      */

     public void sendMessage (String aMessage) {

          cl.getOutput().println (aMessage);

          cl.getOutput().flush();

     }

 

     /**

      * Method handles ActionEvent event, registered by "this" Action listener.

      * @param ae - ActionEvent which we used to indicate "sender".

      */

     public void actionPerformed (ActionEvent ae) {

 

          if (ae.getSource().equals(textField1)) {

// catch ActionEvents coming from textField1

               sendButtonPressed();

          } else if (ae.getSource().equals(connectButton)) {

// catch ActionEvents coming connect button from textField1

               if (!isConnected) {

                    addSystemMessage("Connecting...");

                    isConnected = cl.connect();

               } else {

                    addSystemMessage("Already connected.");

               }

          } else if (ae.getSource().equals(sendButton)) {

// catch ActionEvents coming send button from textField1

               sendButtonPressed();

          } else if (ae.getSource().equals(menuItem1)) {

// catch ActionEvents comming from popupMenu->menuItem1->"Ignore"

               String selectedUser = list1.getSelectedItem();

               deniedUsers.addElement (selectedUser);

               addSystemMessage("User added to ban list.");

          } else if (ae.getSource().equals(menuItem2)) {

// catch ActionEvents comming from popupMenu->menuItem1->"Deignore"

               String selectedUser = list1.getSelectedItem();

               if (!deniedUsers.removeElement (selectedUser))

                    addSystemMessage("No such user in ban list.");

               else

                    addSystemMessage("User removed from ban list.");

          } else if (ae.getSource().equals(clearButton)) {

// catch ActionEvents comming from clear button

               textArea1.setText("");

          } else if (ae.getSource().equals(disconnectButton)) {

// catch ActionEvents comming from disconnect button

               cl.disconnect();

          } else if (ae.getSource().equals(exitButton)) {

// catch ActionEvents comming from exit button

               System.exit(0);

          }

     }

 

     private void sendButtonPressed() {

          if (!isConnected) {

               textArea1.append("Please connect first.\n");

               return;

          }

          String text = textField1.getText();

          if (text.equals(""))

               return;

          sendMessage (text);

          textField1.setText ("");

     }

 

     public void mouseClicked(MouseEvent e) {}

     public void mouseReleased(MouseEvent e) {}

     public void mouseEntered(MouseEvent e) {}

     public void mouseExited(MouseEvent e) {}

 

     /**

      * Method handles mousePressed event, registered by "this" MouseListener.

      * @param e MouseEvent

      */

     public void mousePressed(MouseEvent e) {

          // register when user pressed right mouse button

          // - e.getModifiers()==e.BUTTON3_MASK -

          // and when is "mouse down" - e.MOUSE_PRESSED==501.

          if ((e.getModifiers()==e.BUTTON3_MASK)&&(e.MOUSE_PRESSED==501)) {

               popupMenu1.show (list1, e.getX(), e.getY());

          }

     }

 

}







ChatClient.java


/**

 * Chat applet client for Nakov Chat Server.

 * Author: Nikolay Nedyalkov, 2002

 *

 * ChatClient class handles the communication with the chat server.

 */

import java.io.*;

import java.net.*;

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

import java.util.Hashtable;

 

public class ChatClient

{

     public static final int SERVER_PORT = 2002;

 

     private Socket m_Socket = null;

     private BufferedReader in = null;

     private PrintWriter out = null;

     private ChatApplet m_Applet;

 

     /**

      * Constructor initialize ChatClient and sets ChatApplet refferences.

      */

     public ChatClient (ChatApplet anApplet) {

          this.m_Applet = anApplet;

     }

 

     /**

      * Method is called from ChatApplet.

      * @return PrintWriter reference to server connection.

      */

     public PrintWriter getOutput () {

          return out;

     }

 

     /**

      * Method is called from ChatApplet.

      * @return BufferedReader reference to server connection.

      */

     public BufferedReader getInput () {

          return in;

     }

 

     /**

      * Method is called from ChatApplet to establish connection to NakovChatServer.

      * In case of the applet is started locally from a file (not from a web server),

      * "localhost" is used as taget server, otherwise getCodeBase().getHost() is used.

      */

     public boolean connect () {

          boolean successfull = true;

          String serverHost = m_Applet.getCodeBase().getHost();

          if (serverHost.length()==0) {

               m_Applet.addSystemMessage("Warning: Applet is loaded from a local file,");

               m_Applet.addSystemMessage("not from a web server. Web browser's security");

               m_Applet.addSystemMessage("will probably disable socket connections.");

               serverHost = "localhost";

          }

          try {

               m_Socket = new Socket(serverHost, SERVER_PORT);

               in = new BufferedReader(

                    new InputStreamReader(m_Socket.getInputStream()));

               out = new PrintWriter(

                    new OutputStreamWriter(m_Socket.getOutputStream()));

               m_Applet.addSystemMessage("Connected to server " +

                  serverHost + ":" + SERVER_PORT);

          } catch (SecurityException se) {

               m_Applet.addSystemMessage("Security policy does not allow " +

                    "connection to " + serverHost + ":" + SERVER_PORT);

               successfull = false;

          } catch (IOException e) {

               m_Applet.addSystemMessage("Can not establish connection to " +

                    serverHost + ":" + SERVER_PORT);

               successfull = false;

          }

 

          // Create and start Listener thread

          Listener listener = new Listener(m_Applet, in);

          listener.setDaemon(true);

          listener.start();

 

          return successfull;

     }

 

     public void disconnect() {

          if (!m_Applet.getConnected()) {

               m_Applet.addSystemMessage("Can not disconnect. Not connected.");

               return;

          }

          m_Applet.setConnected(false);

          try {

               m_Socket.close();

          } catch (IOException ioe) {

          }

          m_Applet.addSystemMessage("Disconnected.");

     }

 

     /**

      * Listener class - thread is used for receiving data that comes from

      * the server and then "forward" it to ChatApplet.

      */

     class Listener extends Thread

     {

          private BufferedReader mIn;

          private ChatApplet mCA;

 

          /**

           * Constructor initiliaze InputStream, and ChatApplet reference

           * @param aCA - ChatApplet reference

           * @param aIn - InputStream from server connection

           */

          public Listener (ChatApplet aCA, BufferedReader aIn) {

               mCA = aCA;

               mIn  = aIn;

          }

 

          public void run() {

               try {

                    while (!isInterrupted()) {

                         String message = mIn.readLine();

                         int colon2index = message.indexOf(":",message.indexOf(":")+1);

                         String user = message.substring(0, colon2index-1);

                         mCA.addText (message, user);

                         mCA.addUser (user);

                    }

               } catch (Exception ioe) {

                    if (m_Applet.getConnected())

                         m_Applet.addSystemMessage("Communication error.");

               }

               m_Applet.setConnected(false);

          }

     }

}







ChatApplet.html


<html>

 

<head>

     <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">

     <title>Chat Applet</title>

</head>

 

<body>

 

     ChatApplet will appear below in a Java enabled browser.<br>

 

     <applet

       codebase = "."

       code     = "ChatApplet.class"

       name     = "TestApplet"

       width    = "430"

       height   = "300"

       hspace   = "0"

       vspace   = "0"

       align    = "middle"

     >

     </applet>

    

</body>

 

</html>





출처 : http://www.nakov.com