Java/Tiny Application/Chat
Chat client server
<source lang="java">
/**
* 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])); }
}
</source>
Simple console-mode (command-line) chat client
<source lang="java">
/*
* 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 + "]"; } }
} /**
** 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.
** 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.
** Uses client socket w/ two Threads (main and one constructed), * one for reading and one for writing.
** Server multiplexes messages back to all clients.
* @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(); }
}
</source>