Sunday, July 20, 2008

A complete chat program

ChatClient.java

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

public class ChatClient implements Runnable
{
static String command;
Socket socket;
static final int portNumber=1666;
Thread commReader=null;

BufferedReader input;
PrintStream output;

String alias=null;
boolean logging=true;

public static void main(String[] argv) throws IOException
{
InetAddress clientAddr;
if(argv.length==0)
clientAddr=InetAddress.getLocalHost();
else
clientAddr=InetAddress.getByName(argv[0]);
System.out.println("Connecting to Chat Server at "+clientAddr);
ChatClient cc=new ChatClient(clientAddr);
Thread t=new Thread(cc);
t.start();
}

public ChatClient(InetAddress adx)
{
CommandReader cr=new CommandReader(this);
commReader=new Thread(cr);
command="xXx";
try
{
socket=new Socket(adx,portNumber);
input=new BufferedReader(new InputStreamReader(socket.getInputStream()));
output=new PrintStream(socket.getOutputStream());
commReader.start();
}
catch(IOException e)
{
System.out.println("Abnormal chat client socket condition:"+e);
}
}

public void run()
{
try
{
doLoop();
input.close();
output.close();
socket.close();
System.exit(0);
}
catch(IOException e)
{
System.out.println("Abnormal chat client socket condition:"+e);;
}

}

public void doLoop() throws IOException
{
String nullString=new String("xXx");
String data=nullString;
while(true)
{

synchronized(command)
{

if(!command.equals("xXx"))
{
data=command;
//reset
command="xXx";

}
} //end synch

if(!data.equals("xXx"))
{
if(logging)
{
//first msg
logging=false;
alias=data;
System.out.println(">>Logging as:"+alias);
output.println("S"+alias); //Starting new chat
}
else
{
if(data.equals("exit"))
{
System.out.println(">>Exiting Chat.");
output.println("X"+alias);
break;
}
else if(data.equals("/list"))
{
System.out.println(">>list of connected users:");
output.println("L"+alias);
break;
}
else if(data.equals("/help"))
{
System.out.println(">>Help on Chat commands:");
output.println("H"+alias);
break;
}
else
{
System.out.println(">>Sending msg:"+data);
output.println("D"+data); //sending data
}
}

}

data=input.readLine();
if(!data.equals("xXx"))
{
System.out.println("<<"+data);
}
data=nullString;
}
}

}


ChatDaemon.java

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

public class ChatDaemon implements Runnable
{
static final int maxClients=20;
static final int portNumber=1666;
static int numClients=0;
ChatServer[] chatters;
Thread me;

SharedMsg sharedMsg;

public static void main(String[] argv)
{
ChatDaemon cd=new ChatDaemon();
}

public ChatDaemon()
{
sharedMsg=new SharedMsg(this);
chatters=new ChatServer[maxClients];
//start sending
me=new Thread(this);
me.start();

try
{
listen();
}
catch(IOException e)
{
System.out.println("Abnormal socket condition:"+e);
}
}

public void listen() throws IOException
{
ServerSocket ss=null;
ss=new ServerSocket(portNumber);
Socket chatSocket=null;
System.out.println("...listening for connections..");
while(true)
{
chatSocket=ss.accept();
ChatServer ct=allocThread(chatSocket);
synchronized(System.out)
{
System.out.println("Allocated new chat server");
}
}

}

ChatServer allocThread(Socket s)
{
if(numClients<maxClients)
{
ChatServer ct=new ChatServer(s,sharedMsg,numClients);
chatters[numClients]=ct;
Thread t=new Thread(ct);
t.start();
numClients++;
return ct;
}
else
return null;
}

public void run()
{
char broad=(char)-1;
String dataNull=new String(broad+"xXx");
while(true)
{

for(int i=0;i<numClients;i++)
if(chatters[i].alive())
chatters[i].newMsg(dataNull);
}
}

public void refresh()
{
String data=sharedMsg.get();
synchronized(System.out)
{
System.out.println("RECEIVED:"+data);
}
for(int i=0;i<numClients;i++)
{
if(chatters[i].alive())
chatters[i].newMsg(data);
}
}
}


ChatServer.java

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

public class ChatServer implements Runnable
{
int index=-1;
SharedMsg sharedMsg;
Socket socket;

DataInputStream input;
PrintStream output;

String alias="";
boolean running=false;

boolean alive()
{
return running;
}

public ChatServer(Socket s,SharedMsg sm,int idx)
{
index=idx;
sharedMsg=sm;
socket=s;
try
{
input=new DataInputStream(socket.getInputStream());
output=new PrintStream(socket.getOutputStream(),true);

}
catch(IOException e)
{
System.out.println("Abnormal chat server socket condition 1:"+e);;
}
}

public void run()
{
try
{
doLoop();
input.close();
output.close();
socket.close();
}
catch(IOException e)
{
System.out.println("Abnormal chat server socket condition 2:"+e);;
}
}

public void doLoop() throws IOException
{
String line=null;
String data=null;
char command='W';
char ind=(char)index; //index of sender of msg
char broad=(char)-1; //goes to everybody

running=true;
while(true)
{
//loop: reads in msg, write it onto sharedmsg
line=input.readLine();
command=line.charAt(0);
data=line.substring(1);

if(command=='X')
{
sharedMsg.put(broad+"User: "+data+" Logged Out");
break;
}
else if(command=='S')
{
//first msg
alias=data;
sharedMsg.put(broad+"New User Logged: "+alias);
}
else
{
sharedMsg.put(ind+"From "+alias+": "+data);
}
}
//exiting
running=false;
}

//new message arrived, send it over the socket
public void newMsg(String msg)
{
char mitt=msg.charAt(0);
String data=msg.substring(1);

if(mitt!=(char)index)
{
//send
output.println(data);
output.flush();

synchronized(System.out)
{
System.out.print(".\b");
System.out.flush();
}
}
}
}


CommandReader.java

import java.io.*;

/**
* Reads from keyboard (STDIN)
*/
class CommandReader implements Runnable
{
ChatClient chatClient;
DataInputStream user;

public CommandReader(ChatClient cc)
{
chatClient=cc;
user=new DataInputStream(System.in);
}

public void run()
{
System.out.println("Waiting for client side commands...");

String com=null;
while(true)
{
//reads from keyboard
try
{
com=user.readLine();
}
catch(IOException e)
{
System.out.println("Abnormal reading from user:"+e);
}

synchronized(chatClient.command)
{
chatClient.command=com;
}
}
}
}


SharedMsg.java

public class SharedMsg
{
String data="";
ChatDaemon chatDaemon=null;

SharedMsg(ChatDaemon cd)
{
chatDaemon=cd;

}

synchronized String get()
{
return data;
}

synchronized void put(String s)
{
data=s;
chatDaemon.refresh();
}
}