/*
* Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 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.com/
* @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("atlas.dsv.su.se", 9494);
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.com/
* @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.
* 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(); } }