Java/Tiny Application/Chat

Материал из Java эксперт
Перейти к: навигация, поиск

Chat client server

 
/**
 * ChatClient.java  1.00 96/11/07 Merlin Hughes
 *
 * Copyright (c) 1996 Prominence Dot Com, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * for non-commercial purposes and without fee is hereby granted
 * provided that this copyright notice appears in all copies.
 *
 * http://prominence.ru/                  merlin@prominence.ru
 */
 
import java.net.*;
import java.io.*;
import java.awt.*;
public class ChatClient extends Frame implements Runnable {
  protected DataInputStream i;
  protected DataOutputStream o;
  protected TextArea output;
  protected TextField input;
  protected Thread listener;
  public ChatClient (String title, InputStream i, OutputStream o) {
    super (title);
    this.i = new DataInputStream (new BufferedInputStream (i));
    this.o = new DataOutputStream (new BufferedOutputStream (o));
    setLayout (new BorderLayout ());
    add ("Center", output = new TextArea ());
    output.setEditable (false);
    add ("South", input = new TextField ());
    pack ();
    show ();
    input.requestFocus ();
    listener = new Thread (this);
    listener.start ();
  }
  public void run () {
    try {
      while (true) {
        String line = i.readUTF ();
        output.appendText (line + "\n");
      }
    } catch (IOException ex) {
      ex.printStackTrace ();
    } finally {
      listener = null;
      input.hide ();
      validate ();
      try {
        o.close ();
      } catch (IOException ex) {
        ex.printStackTrace ();
      }
    }
  }
  public boolean handleEvent (Event e) {
    if ((e.target == input) && (e.id == Event.ACTION_EVENT)) {
      try {
        o.writeUTF ((String) e.arg);
        o.flush ();
      } catch (IOException ex) {
        ex.printStackTrace();
        listener.stop ();
      }
      input.setText ("");
      return true;
    } else if ((e.target == this) && (e.id == Event.WINDOW_DESTROY)) {
      if (listener != null)
        listener.stop ();
      hide ();
      return true;
    }
    return super.handleEvent (e);
  }
  public static void main (String args[]) throws IOException {
    if (args.length != 2)
      throw new RuntimeException ("Syntax: ChatClient <host> <port>");
    Socket s = new Socket (args[0], Integer.parseInt (args[1]));
    new ChatClient ("Chat " + args[0] + ":" + args[1],
                    s.getInputStream (), s.getOutputStream ());
  }
}

////////

/**
 * ChatApplet.java  1.00 96/11/01 Merlin Hughes
 *
 * Copyright (c) 1996 Prominence Dot Com, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * for non-commercial purposes and without fee is hereby granted
 * provided that this copyright notice appears in all copies.
 *
 * http://prominence.ru/                  merlin@prominence.ru
 */
import java.net.*;
import java.io.*;
import java.awt.*;
import java.applet.*;
// Applet parameters:
//   host = host name
//   port = host port
public class ChatApplet extends Applet implements Runnable {
  protected DataInputStream i;
  protected DataOutputStream o;
  protected TextArea output;
  protected TextField input;
  protected Thread listener;
  public void init () {
    setLayout (new BorderLayout ());
    add ("Center", output = new TextArea ());
    output.setEditable (false);
    add ("South", input = new TextField ());
    input.setEditable (false);
  }
  public void start () {
    listener = new Thread (this);
    listener.start ();
  }
  public void stop () {
    if (listener != null)
      listener.stop ();
    listener = null;
  }
  public void run () {
    try {
      String host = getParameter ("host");
      if (host == null)
  host = getCodeBase ().getHost ();
      String port = getParameter ("port");
      if (port == null)
  port = "9830";
      output.appendText ("Connecting to " + host + ":" + port + "...");
      Socket s = new Socket (host, Integer.parseInt (port));
      i = new DataInputStream (new BufferedInputStream (s.getInputStream ()));
      o = new DataOutputStream (new BufferedOutputStream (s.getOutputStream ()));
      output.appendText (" connected.\n");
      input.setEditable (true);
      input.requestFocus ();
      execute ();
    } catch (IOException ex) {
      ByteArrayOutputStream out = new ByteArrayOutputStream ();
      ex.printStackTrace (new PrintStream (out));
      output.appendText ("\n" + out);
    }
  }
  public void execute () {
    try {
      while (true) {
        String line = i.readUTF ();
        output.appendText (line + "\n");
      }
    } catch (IOException ex) {
      ByteArrayOutputStream out = new ByteArrayOutputStream ();
      ex.printStackTrace (new PrintStream (out));
      output.appendText (out.toString ());
    } finally {
      listener = null;
      input.hide ();
      validate ();
      try {
        o.close ();
      } catch (IOException ex) {
        ex.printStackTrace ();
      }
    }
  }
  public boolean handleEvent (Event e) {
    if ((e.target == input) && (e.id == Event.ACTION_EVENT)) {
      try {
        o.writeUTF ((String) e.arg);
        o.flush ();
      } catch (IOException ex) {
        ex.printStackTrace();
        listener.stop ();
      }
      input.setText ("");
      return true;
    } else if ((e.target == this) && (e.id == Event.WINDOW_DESTROY)) {
      if (listener != null)
        listener.stop ();
      hide ();
      return true;
    }
    return super.handleEvent (e);
  }
}

///////////
/**
 * ChatHandler.java  1.00 96/11/07 Merlin Hughes
 *
 * Copyright (c) 1996 Prominence Dot Com, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * for non-commercial purposes and without fee is hereby granted
 * provided that this copyright notice appears in all copies.
 *
 * http://prominence.ru/                  merlin@prominence.ru
 */
 
import java.net.*;
import java.io.*;
import java.util.*;
public class ChatHandler extends Thread {
  protected Socket s;
  protected DataInputStream i;
  protected DataOutputStream o;
  public ChatHandler (Socket s) throws IOException {
    this.s = s;
    i = new DataInputStream (new BufferedInputStream (s.getInputStream ()));
    o = new DataOutputStream (new BufferedOutputStream (s.getOutputStream ()));
  }
  protected static Vector handlers = new Vector ();
  public void run () {
    String name = s.getInetAddress ().toString ();
    try {
      broadcast (name + " has joined.");
      handlers.addElement (this);
      while (true) {
        String msg = i.readUTF ();
        broadcast (name + " - " + msg);
      }
    } catch (IOException ex) {
      ex.printStackTrace ();
    } finally {
      handlers.removeElement (this);
      broadcast (name + " has left.");
      try {
        s.close ();
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
  }
  protected static void broadcast (String message) {
    synchronized (handlers) {
      Enumeration e = handlers.elements ();
      while (e.hasMoreElements ()) {
        ChatHandler c = (ChatHandler) e.nextElement ();
        try {
          synchronized (c.o) {
            c.o.writeUTF (message);
          }
          c.o.flush ();
        } catch (IOException ex) {
          c.stop ();
        }
      }
    }
  }
}
///////////
/**
 * ChatServer.java  1.00 96/11/07 Merlin Hughes
 *
 * Copyright (c) 1996 Prominence Dot Com, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * for non-commercial purposes and without fee is hereby granted
 * provided that this copyright notice appears in all copies.
 *
 * http://prominence.ru/                  merlin@prominence.ru
 */
 
import java.net.*;
import java.io.*;
import java.util.*;
public class ChatServer {
  public ChatServer (int port) throws IOException {
    ServerSocket server = new ServerSocket (port);
    while (true) {
      Socket client = server.accept ();
      System.out.println ("Accepted from " + client.getInetAddress ());
      ChatHandler c = new ChatHandler (client);
      c.start ();
    }
  }
  public static void main (String args[]) throws IOException {
    if (args.length != 1)
      throw new RuntimeException ("Syntax: ChatServer <port>");
    new ChatServer (Integer.parseInt (args[0]));
  }
}





Simple console-mode (command-line) chat client

 
/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import java.net.*;
import java.util.*;
/** Simple console-mode (command-line) chat client.
 * @author Ian Darwin, http://www.darwinsys.ru/
 * @version $Id: ConsChat.java,v 1.6 2004/02/16 02:44:43 ian Exp $
 */
public class ConsChat {
  public static void main(String[] args) throws IOException {
    new ConsChat().chat();
  }
  protected Socket sock;
  protected BufferedReader is;
  protected PrintWriter pw;
  protected BufferedReader cons;
  protected ConsChat() throws IOException {
    sock = new Socket("localhost", Chat.PORTNUM);
    is   = new BufferedReader(new InputStreamReader(sock.getInputStream()));
    pw   = new PrintWriter(sock.getOutputStream(), true);
    cons = new BufferedReader(new InputStreamReader(System.in));
    // Construct and start the reader: from server to stdout.
    // Make a Thread to avoid lockups.
    new Thread() {
      public void run() {
        setName("socket reader thread");
        System.out.println("Starting " + getName());
        System.out.flush();
        String line;
        try {
          // reader thread blocks here
          while ((line = is.readLine()) != null) {
            System.out.println(line);
            System.out.flush();
          }
        } catch (IOException ex) {
          System.err.println("Read error on socket: " + ex);
          return;
        }
      }
    }.start();
  }
  protected void chat() throws IOException {
    String text;
    System.out.print("Login name: "); System.out.flush();
    text = cons.readLine();
    send(Chat.CMD_LOGIN + text);
    // Main thread blocks here
    while ((text = cons.readLine()) != null) {
      if (text.length() == 0 || text.charAt(0) == "#")
        continue;      // ignore null lines and comments
      if (text.charAt(0) == "/")
        send(text.substring(1));
      else send("B"+text);
    }
  }
  protected void send(String s) {
    pw.println(s);
    pw.flush();
  }
}
/** Constants and Class Methods for Java Chat Clients and Server.
 *
 * The protocol:
 *  --> Lusername
 *  --> Btext_to_broadcast
 *  --> Musername\Message
 *  --> Q
 *  <-- any text to be displayed.
 *
 * @author Ian Darwin
 * @version $Id: Chat.java,v 1.3 2004/02/16 02:44:43 ian Exp $
 */
class Chat {
  // These are the first character of messages from client to server
  public static final int PORTNUM = 9999;
  public static final int MAX_LOGIN_LENGTH = 20;
  public static final char SEPARATOR = "\\";
  public static final char COMMAND = "\\";
  public static final char CMD_LOGIN = "L";
  public static final char CMD_QUIT  = "Q";
  public static final char CMD_MESG  = "M";
  public static final char CMD_BCAST = "B";
  // These are the first character of messages from server to client
  public static final char RESP_PUBLIC = "P";
  public static final char RESP_PRIVATE = "M";
  public static final char RESP_SYSTEM = "S";
  // TODO in main loop:
  // if (text.charAt(0) == "/")
  //    send(text);
  // else send("B"+text);
  public static boolean isValidLoginName(String login) {
    // check length
    if (login.length() > MAX_LOGIN_LENGTH)
      return false;
    // check for bad chars
    // if (contains bad chars)
    //  return false
    // Passed above tests, is OK
    return true;
  }
}
/** Trivial Chat Server to go with our Trivial Chat Client.
 *
 * WARNING -- this code is believed thread-safe but has NOT been 100% vetted 
 * by a team of world-class experts for Thread-safeness.
 * DO NOT BUILD ANYTHING CRITICAL BASED ON THIS until you have done so.
 * See the various books on Threaded Java for design issues.
 *
 * @author  Ian F. Darwin, http://www.darwinsys.ru/
 * @version $Id: ChatServer.java,v 1.10 2004/03/13 21:56:32 ian Exp $
 */
class ChatServer {
  /** What I call myself in system messages */
  protected final static String CHATMASTER_ID = "ChatMaster";
  /** What goes between any handle and the message */
  protected final static String SEP = ": ";
  /** The Server Socket */
  protected ServerSocket servSock;
  /** The list of my current clients */
  protected ArrayList clients;
  /** Debugging state */
  private static boolean DEBUG = false;
  /** Main just constructs a ChatServer, which should never return */
  public static void main(String[] argv) {
    System.out.println("DarwinSys Chat Server 0.1 starting...");
    if (argv.length == 1 && argv[0].equals("-debug"))
      DEBUG = true;
    ChatServer w = new ChatServer();
    w.runServer();      // should never return.
    System.out.println("**ERROR* Chat Server 0.1 quitting");
  }
  /** Construct (and run!) a Chat Service */
  ChatServer() {
    clients = new ArrayList();
    try {
      servSock = new ServerSocket(Chat.PORTNUM);
      System.out.println("DarwinSys Chat Server Listening on port " +
        Chat.PORTNUM);
    } catch(IOException e) {
      log("IO Exception in ChatServer.<init>" + e);
      System.exit(0);
    }
  }
  public void runServer() {
    try {
      while (true) {
        Socket us = servSock.accept();
        String hostName = us.getInetAddress().getHostName();
        System.out.println("Accepted from " + hostName);
        ChatHandler cl = new ChatHandler(us, hostName);
        synchronized (clients) {
          clients.add(cl);
          cl.start();
          if (clients.size() == 1)
            cl.send(CHATMASTER_ID, "Welcome! you"re the first one here");
          else {
            cl.send(CHATMASTER_ID, "Welcome! you"re the latest of " +
              clients.size() + " users.");
          }
        }
      }
    } catch(IOException e) {
      log("IO Exception in runServer: " + e);
      System.exit(0);
    }
  }
  protected void log(String s) {
    System.out.println(s);
  }
  /** Inner class to handle one conversation */
  protected class ChatHandler extends Thread {
    /** The client socket */
    protected Socket clientSock;
    /** BufferedReader for reading from socket */
    protected BufferedReader is;
    /** PrintWriter for sending lines on socket */
    protected PrintWriter pw;
    /** The client"s host */
    protected String clientIP;
    /** String form of user"s handle (name) */
    protected String login;
    /* Construct a Chat Handler */
    public ChatHandler(Socket sock, String clnt) throws IOException {
      clientSock = sock;
      clientIP = clnt;
      is = new BufferedReader(
        new InputStreamReader(sock.getInputStream()));
      pw = new PrintWriter(sock.getOutputStream(), true);
    }
    /** Each ChatHandler is a Thread, so here"s the run() method,
     * which handles this conversation.
     */
    public void run() {
      String line;
      try {
        while ((line = is.readLine()) != null) {
          char c = line.charAt(0);
          line = line.substring(1);
          switch (c) {
          case Chat.CMD_LOGIN:
            if (!Chat.isValidLoginName(line)) {
              send(CHATMASTER_ID, "LOGIN " + line + " invalid");
              log("LOGIN INVALID from " + clientIP);
              continue;
            }
            login = line;
            broadcast(CHATMASTER_ID, login + 
              " joins us, for a total of " + 
              clients.size() + " users");
            break;
          case Chat.CMD_MESG:
            if (login == null) {
              send(CHATMASTER_ID, "please login first");
              continue;
            }
            int where = line.indexOf(Chat.SEPARATOR);
            String recip = line.substring(0, where);
            String mesg = line.substring(where+1);
            log("MESG: " + login + "-->" + recip + ": "+ mesg);
            ChatHandler cl = lookup(recip);
            if (cl == null)
              psend(CHATMASTER_ID, recip + " not logged in.");
            else
              cl.psend(login, mesg);
            break;
          case Chat.CMD_QUIT:
            broadcast(CHATMASTER_ID,
              "Goodbye to " + login + "@" + clientIP);
            close();
            return;    // The end of this ChatHandler
            
          case Chat.CMD_BCAST:
            if (login != null)
              broadcast(login, line);
            else
              log("B<L FROM " + clientIP);
            break;
          default:
            log("Unknown cmd " + c + " from " + login + "@" + clientIP);
          }
        }
      } catch (IOException e) {
        log("IO Exception: " + e);
      } finally {
        // the sock ended, so we"re done, bye now
        System.out.println(login + SEP + "All Done");
        synchronized(clients) {
          clients.remove(this);
          if (clients.size() == 0) {
            System.out.println(CHATMASTER_ID + SEP +
              "I"m so lonely I could cry...");
          } else if (clients.size() == 1) {
            ChatHandler last = (ChatHandler)clients.get(0);
            last.send(CHATMASTER_ID,
              "Hey, you"re talking to yourself again");
          } else {
            broadcast(CHATMASTER_ID,
              "There are now " + clients.size() + " users");
          }
        }
      }
    }
    protected void close() {
      if (clientSock == null) {
        log("close when not open");
        return;
      }
      try {
        clientSock.close();
        clientSock = null;
      } catch (IOException e) {
        log("Failure during close to " + clientIP);
      }
    }
    /** Send one message to this user */
    public void send(String sender, String mesg) {
      pw.println(sender + SEP + mesg);
    }
    /** Send a private message */
    protected void psend(String sender, String msg) {
      send("<*" + sender + "*>", msg);
    }
    
    /** Send one message to all users */
    public void broadcast(String sender, String mesg) {
      System.out.println("Broadcasting " + sender + SEP + mesg);
      for (int i=0; i<clients.size(); i++) {
        ChatHandler sib = (ChatHandler)clients.get(i);
        if (DEBUG)
          System.out.println("Sending to " + sib);
        sib.send(sender, mesg);
      }
      if (DEBUG) System.out.println("Done broadcast");
    }
    protected ChatHandler lookup(String nick) {
      synchronized(clients) {
        for (int i=0; i<clients.size(); i++) {
          ChatHandler cl = (ChatHandler)clients.get(i);
          if (cl.login.equals(nick))
            return cl;
        }
      }
      return null;
    }
    /** Present this ChatHandler as a String */
    public String toString() {
      return "ChatHandler[" + login + "]";
    }
  }
}
/** 
 * <p>
 * Simple Chat Room Applet.
 * Writing a Chat Room seems to be one of many obligatory rites (or wrongs)
 * of passage for Java experts these days.</p>
 * <p>
 * This one is a toy because it doesn"t have much of a protocol, which
 * means we can"t query the server as to * who"s logged in,
 * or anything fancy like that. However, it works OK for small groups.</p>
 * <p>
 * Uses client socket w/ two Threads (main and one constructed),
 * one for reading and one for writing.</p>
 * <p>
 * Server multiplexes messages back to all clients.</p>
 * @author Ian Darwin
 * @version $Id: ChatRoom.java,v 1.8 2004/03/09 03:59:37 ian Exp $
 */
class ChatRoom extends Applet {
  /** Whether we are being run as an Applet or an Application */
  protected boolean inAnApplet = true;
  /** The state of logged-in-ness */
  protected boolean loggedIn;
  /* The Frame, for a pop-up, durable Chat Room. */
  protected Frame cp;
  /** The default port number */
  protected static int PORTNUM = Chat.PORTNUM;
  /** The actual port number */
  protected int port;
  /** The network socket */
  protected Socket sock;
  /** BufferedReader for reading from socket */
  protected BufferedReader is;
  /** PrintWriter for sending lines on socket */
  protected PrintWriter pw;
  /** TextField for input */
  protected TextField tf;
  /** TextArea to display conversations */
  protected TextArea ta;
  /** The Login button */
  protected Button lib;
  /** The LogOUT button */
  protected Button lob;
  /** The TitleBar title */
  final static String TITLE = "Chat: Ian Darwin"s Toy Chat Room Client";
  /** The message that we paint */
  protected String paintMessage;
  /** init, overriding the version inherited from Applet */
  public void init() {
    paintMessage = "Creating Window for Chat";
    repaint();
    cp = new Frame(TITLE);
    cp.setLayout(new BorderLayout());
    String portNum = null;
    if (inAnApplet)
      portNum = getParameter("port");
    port = PORTNUM;
    if (portNum != null)
      port = Integer.parseInt(portNum);
    // The GUI
    ta = new TextArea(14, 80);
    ta.setEditable(false);    // readonly
    ta.setFont(new Font("Monospaced", Font.PLAIN, 11));
    cp.add(BorderLayout.NORTH, ta);
    Panel p = new Panel();
    Button b;
    // The login button
    p.add(lib = new Button("Login"));
    lib.setEnabled(true);
    lib.requestFocus();
    lib.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        login();
        lib.setEnabled(false);
        lob.setEnabled(true);
        tf.requestFocus();  // set keyboard focus in right place!
      }
    });
    // The logout button
    p.add(lob = new Button("Logout"));
    lob.setEnabled(false);
    lob.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        logout();
        lib.setEnabled(true);
        lob.setEnabled(false);
        lib.requestFocus();
      }
    });
    p.add(new Label("Message here:"));
    tf = new TextField(40);
    tf.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (loggedIn) {
          pw.println(Chat.CMD_BCAST+tf.getText());
          tf.setText(""); 
        }
      }
    });
    p.add(tf);
    cp.add(BorderLayout.SOUTH, p);
        cp.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        // If we do setVisible and dispose, then the Close completes
        ChatRoom.this.cp.setVisible(false);
        ChatRoom.this.cp.dispose();
        logout();
      }
    });
    cp.pack();
    // After packing the Frame, centre it on the screen.
    Dimension us = cp.getSize(), 
      them = Toolkit.getDefaultToolkit().getScreenSize();
    int newX = (them.width - us.width) / 2;
    int newY = (them.height- us.height)/ 2;
    cp.setLocation(newX, newY);
    cp.setVisible(true);
    paintMessage = "Window should now be visible";
    repaint();
  }
  protected String serverHost = "localhost";
  /** LOG ME IN TO THE CHAT */
  public void login() {
    showStatus("In login!");
    if (loggedIn)
      return;
    if (inAnApplet)
      serverHost = getCodeBase().getHost();
    try {
      sock = new Socket(serverHost, port);
      is = new BufferedReader(new InputStreamReader(sock.getInputStream()));
      pw = new PrintWriter(sock.getOutputStream(), true);
    } catch(IOException e) {
      showStatus("Can"t get socket to " + 
        serverHost + "/" + port + ": " + e);
      cp.add(new Label("Can"t get socket: " + e));
      return;
    }
    showStatus("Got socket");
    // Construct and start the reader: from server to textarea.
    // Make a Thread to avoid lockups.
    new Thread(new Runnable() {
      public void run() {
        String line;
        try {
          while (loggedIn && ((line = is.readLine()) != null))
            ta.append(line + "\n");
        } catch(IOException e) {
          showStatus("GAA! LOST THE LINK!!");
          return;
        }
      }
    }).start();
    // FAKE LOGIN FOR NOW
    pw.println(Chat.CMD_LOGIN + "AppletUser");
    loggedIn = true;
  }
  /** Log me out, Scotty, there"s no intelligent life here! */
  public void logout() {
    if (!loggedIn)
      return;
    loggedIn = false;
    try {
      if (sock != null)
        sock.close();
    } catch (IOException ign) {
      // so what?
    }
  }
  // It is deliberate that there is no STOP method - we want to keep
  // going even if the user moves the browser to another page.
  // Anti-social? Maybe, but you can use the CLOSE button to kill 
  // the Frame, or you can exit the Browser.
  /** Paint paints the small window that appears in the HTML,
   * telling the user to look elsewhere!
   */
  public void paint(Graphics g) {
    Dimension d = getSize();
    int h = d.height;
    int w = d.width;
    g.fillRect(0, 0, w, 0);
    g.setColor(Color.black);
    g.drawString(paintMessage, 10, (h/2)-5);
  }

  /** a showStatus that works for Applets or non-Applets alike */
  public void showStatus(String mesg) {
    if (inAnApplet)
      super.showStatus(mesg);
    System.out.println(mesg);
  }
  /** A main method to allow the client to be run as an Application */
  public static void main(String[] args) {
    ChatRoom room101 = new ChatRoom();
    room101.inAnApplet = false;
    room101.init();
    room101.start();
  }
}