This is the server part of the chat program. I assume that you already know some basic network programming, if you don't, you can read my network tutorial. You will need to understand the echoserver and the echoclient.
import java.net.*;The new thing here is the Thread group, we will need this to identify the chatroom. If you want to modify this program, you can make more ThreadGroups to make multiple rooms. Also we need a number to keep track of the connections.
import java.io.*;
public class ChatServer extends Thread{
public final int default_PORT=8000;
Socket sock;
ServerSocket server;
ThreadGroup group;
int nr;
public Server(int port){Now we initialise the ThreadGroup and we give it the name "Chat", but you may ofcourse change that. Other than that we sets nr to 0, and initialize the server to listen to port 8000, if the port number passed to the constructor is 0 and the actuall number if it is greater than 0.group=new ThreadGroup("Chat");
nr=0;
try{
if(port<=0)
server=new ServerSocket(default_PORT);
else
server=new ServerSocket(port);
}
catch(IOException ex){}
}
public void run(){First we use the Thread method setPriority(int) to set the priority to 3. This isn't necesary, but the default priority is 5, so we set it down to 3 to make the other programs run better. I tried to run this program with max priority (Tread.MAX_PRIORITY = 10) and that caused the entire system to run extremely slowly, because it was busy executing the server (it took about 2 minutes from the time I clicked to activate a window untill it apeared).setPriority(3);
while(true){
try{
sock=server.accept();
Connection con=new Connection(sock,group,nr);
System.out.println("Connection recieved");
nr++;
con.start();
}
catch(IOException ex){}
}
}
public static void main(String args[]){Here we just Start up the server program, and make it listen to port 8000 if none or an illegal portnumber has been passed at the command line, otherwise we start up at the userspecified port.int port=0;
if(args.length<1){
System.out.println("\nServer will listen to port 8000");
}
else{
try{
port=Integer.parseInt(args[0]);
}
catch(NumberFormatException e){
System.out.println("Port argument must be a Integer");
System.out.println("Server will listen to port 8000");
}}
Server s=new Server(port);
s.start();
}
}
class Connection extends Thread{This is the second class of the Server program, I called it Connection, but this is a bad name since there is an interface in another package which is called Connection. You may wanna change it, but it will work out fine this way too.Socket sock;
InputStream in;
OutputStream out;
We need a socket, an InputStream and a OutputStream, to communicate. This class also extends the Thread class, so that it can belong to a ThreadGroup.
public Connection(Socket s,ThreadGroup tg,int nr){This constructor takes Three arguments, a socket, The ThreadGroup to which it shall belong, and the number of the connection (This is just so that all the threads will get different names). First we call the constructor of the superclass with the super(ThreadGroup,String) call. The call to the constructor of the super class must always be the first call of the constructor, if not java will automaticly call the default constructor (the one that takes no arguments). I this case that would be no good, because we want to assign this thread to a group. This Group will be the same for every connection. In the try/catch clause we just assign the input and output Streams.super(tg,"connection nr:"+nr);
sock=s;
try{
in=sock.getInputStream();
out=sock.getOutputStream();
}
catch(IOException ex){}
}
public void run(){Again we divide this method in two since it so long. First we make and empty String and an int which we give the value 0. Then we make the try statement first and then we the infinite while loop. This is important, because if an exception appears, we must jump out of the while loop (of course it could be done different, but this is the easiest way in my opinion). Then we read from the inputsocket, one value (int) at the time, convert it to a char and add it to the String. This goes on untill we reciev newline character, if we don't recieve that (ie. none of the clients are sending data) the thread will block. This is actually a good thing, because if a client closes the connection while the method is reading from the stream, we will get an exception and we are able to get out of "infinite" while loop.String line="";
int tmp=0;
try{
while(true){while((tmp=in.read())!=(char)'\n'){
line+=String.valueOf((char)tmp);
}
if(!(line.equals(""))){Now we must send the string entered to all the clients connected. We will only bother to do this as long as we have a line to send (the if statement), this will prevent the user from sending a lot of empty lines, bothering the other users (You have probably seen some people getting a kick out of hitting return a lot of times, making it imposible for the other users to read each others messages). Next we pass the line to the send method, after that one is excuted the program will continue with erasing the line. At last, if we get an exception, we clean up.
send(line);
line="";
}
}
}
catch(IOException ex){}
finally{try{
in.close();
out.close();
sock.close();}
catch(IOException e){}}//end finally
}
public void send(String line){Now for the send method, here we demonstrate some of the beuty about multithreading. First assign this thread group to an variable called g, then we find the number of characters in the line (I called this variable "lengde" which is the Norwegian word for length). The variable "ant", which we get from the method ThreadGroup.activeCount(), is the number of threads in this threadgroup who are active, we need this soon. Now we create an array of Connection objects, the number of elements in this array is, as you may see, the same number as there are active threads. The enumerate(thread[], boolean) method (it actually returns an int, but we don't care about that) copies each thread into the array, if the array is shorter than the number of threads (Its not here, because we got the number for the array from the ThreadGroup itself), the last threads will just be ignored. You may notice that I use the name Connection and thread as if they were the same. Well you're right, I do, thats because Connection is a thread. Just remember that a thread don't have to be a connection (it depends on the subclassing, but don't worry about it, it may get rather confusing). Next we run through a for so we can call the write method in each thread/connection object, we must pass the line and the length of the line, I'll show you why now.ThreadGroup g=getThreadGroup();
int lengde=line.length();
int ant=g.activeCount();
Connection c[]=new Connection[ant];
g.enumerate(c,false);
for(int i=0;i<ant;i++)
c[i].write(line,lengde);
}
public void write(String line,int lengde){This is the write method, the reason for passing the length of the line is that the for loop will write that many character to the OutputStream. When all the characters in the line has been written, we need to write a newline character, since we use this to show that the line has come to an end.try{
for(int i=0;i<lengde;i++)
out.write((int)line.charAt(i));
out.write((int)'\n');
}
catch(IOException e){}
}
}//End of class