FileDocCategorySizeDatePackage
ChatServer.javaAPI DocExample6533Sat Mar 13 15:56:32 GMT 2004chat

ChatServer.java

package chat;

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

/** 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 $
 */
public 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 + "]";
		}
	}
}