// Server.java
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
/*
* The server that can be run both as a console application
or a GUI
*/
public class Server {
// a unique ID for
each connection
private static int
uniqueId;
// an ArrayList to
keep the list of the Client
private
ArrayList<ClientThread> al;
// if I am in a
GUI
private ServerGUI
sg;
// to display time
private
SimpleDateFormat sdf;
// the port number
to listen for connection
private int port;
// the boolean
that will be turned of to stop the server
private boolean
keepGoing;
/*
* server constructor that receive the port to
listen to for connection as parameter
* in console
*/
public Server(int
port) {
this(port,
null);
}
public Server(int
port, ServerGUI sg) {
// GUI or not
this.sg = sg;
// the port
this.port =
port;
// to display
hh:mm:ss
sdf = new
SimpleDateFormat("HH:mm:ss");
// ArrayList
for the Client list
al = new
ArrayList<ClientThread>();
}
public void
start() {
keepGoing =
true;
/* create
socket server and wait for connection requests */
try
{
// the
socket used by the server
ServerSocket serverSocket = new ServerSocket(port);
//
infinite loop to wait for connections
while(keepGoing)
{
//
format message saying we are waiting
display("Server waiting for Clients on port " + port +
".");
Socket
socket = serverSocket.accept(); //
accept connection
// if
I was asked to stop
if(!keepGoing)
break;
ClientThread t = new ClientThread(socket); // make a thread of it
al.add(t); // save it in
the ArrayList
t.start();
}
// I was
asked to stop
try {
serverSocket.close();
for(int i = 0; i < al.size(); ++i) {
ClientThread tc = al.get(i);
try {
tc.sInput.close();
tc.sOutput.close();
tc.socket.close();
}
catch(IOException ioE) {
// not much I can do
}
}
}
catch(Exception
e) {
display("Exception closing the server and clients: " + e);
}
}
// something
went bad
catch
(IOException e) {
String msg
= sdf.format(new Date()) + " Exception on new ServerSocket: " + e +
"\n";
display(msg);
}
}
/*
* For the GUI to
stop the server
*/
protected void
stop() {
keepGoing =
false;
// connect to
myself as Client to exit statement
// Socket
socket = serverSocket.accept();
try {
new
Socket("localhost", port);
}
catch(Exception e) {
// nothing
I can really do
}
}
/*
* Display an
event (not a message) to the console or the GUI
*/
private void
display(String msg) {
String time =
sdf.format(new Date()) + " " + msg;
if(sg == null)
System.out.println(time);
else
sg.appendEvent(time + "\n");
}
/*
* to broadcast a message to all Clients
*/
private
synchronized void broadcast(String message) {
// add HH:mm:ss
and \n to the message
String time =
sdf.format(new Date());
String
messageLf = time + " " + message + "\n";
// display
message on console or GUI
if(sg == null)
System.out.print(messageLf);
else
sg.appendRoom(messageLf); //
append in the room window
// we loop in
reverse order in case we would have to remove a Client
// because it
has disconnected
for(int i = al.size();
--i >= 0;) {
ClientThread ct = al.get(i);
// try to
write to the Client if it fails remove it from the list
if(!ct.writeMsg(messageLf)) {
al.remove(i);
display("Disconnected Client " + ct.username + " removed
from list.");
}
}
}
// for a client
who logoff using the LOGOUT message
synchronized void
remove(int id) {
// scan the
array list until we found the Id
for(int i = 0;
i < al.size(); ++i) {
ClientThread ct = al.get(i);
// found
it
if(ct.id
== id) {
al.remove(i);
return;
}
}
}
/*
* To run as a console application just open a
console window and:
* > java
Server
* > java
Server portNumber
* If the port
number is not specified 1500 is used
*/
public static void main(String[] args) {
// start
server on port 1500 unless a PortNumber is specified
int portNumber
= 1500;
switch(args.length) {
case 1:
try {
portNumber = Integer.parseInt(args[0]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java Server [portNumber]");
return;
}
case 0:
break;
default:
System.out.println("Usage is: > java Server [portNumber]");
return;
}
// create a
server object and start it
Server server
= new Server(portNumber);
server.start();
}
/** One instance
of this thread will run for each client */
class ClientThread
extends Thread {
// the socket
where to listen/talk
Socket socket;
ObjectInputStream sInput;
ObjectOutputStream sOutput;
// my unique
id (easier for deconnection)
int id;
// the Username of the Client
String
username;
// the only
type of message a will receive
ChatMessage
cm;
// the date I
connect
String date;
//
Constructore
ClientThread(Socket
socket) {
// a
unique id
id =
++uniqueId;
this.socket = socket;
/*
Creating both Data Stream */
System.out.println("Thread trying to create Object Input/Output
Streams");
try
{
//
create output first
sOutput = new ObjectOutputStream(socket.getOutputStream());
sInput = new
ObjectInputStream(socket.getInputStream());
// read the username
username = (String) sInput.readObject();
display(username + " just connected.");
}
catch
(IOException e) {
display("Exception creating new Input/output Streams: " + e);
return;
}
// have to
catch ClassNotFoundException
// but I
read a String, I am sure it will work
catch
(ClassNotFoundException e) {
}
date = new
Date().toString() + "\n";
}
// what will
run forever
public void
run() {
// to loop
until LOGOUT
boolean
keepGoing = true;
while(keepGoing) {
//
read a String (which is an object)
try {
cm
= (ChatMessage) sInput.readObject();
}
catch
(IOException e) {
display(username + " Exception reading
Streams: " + e);
break;
}
catch(ClassNotFoundException e2) {
break;
}
// the
messaage part of the ChatMessage
String
message = cm.getMessage();
//
Switch on the type of message receive
switch(cm.getType()) {
case
ChatMessage.MESSAGE:
broadcast(username + ": " + message);
break;
case
ChatMessage.LOGOUT:
display(username + " disconnected with a LOGOUT message.");
keepGoing = false;
break;
case
ChatMessage.WHOISIN:
writeMsg("List of the users connected at " + sdf.format(new
Date()) + "\n");
//
scan al the users connected
for(int i = 0; i < al.size(); ++i) {
ClientThread ct = al.get(i);
writeMsg((i+1) + ") " + ct.username + " since " +
ct.date);
}
break;
}
}
// remove
myself from the arrayList containing the list of the
//
connected Clients
remove(id);
close();
}
// try to
close everything
private void
close() {
// try to
close the connection
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {}
try {
if(sInput != null)
sInput.close();
}
catch(Exception e) {};
try {
if(socket != null) socket.close();
}
catch
(Exception e) {}
}
/*
* Write a
String to the Client output stream
*/
private
boolean writeMsg(String msg) {
// if
Client is still connected send the message to it
if(!socket.isConnected()) {
close();
return
false;
}
// write
the message to the stream
try {
sOutput.writeObject(msg);
}
// if an
error occurs, do not abort just inform the user
catch(IOException e) {
display("Error sending message to " + username);
display(e.toString());
}
return
true;
}
}
}
Client.java
import java.net.*;
import java.io.*;
import java.util.*;
/*
* The Client that can be run both as a console or a GUI
*/
public class Client {
// for I/O
private ObjectInputStream
sInput; // to read from the socket
private
ObjectOutputStream sOutput; // to
write on the socket
private Socket
socket;
// if I use a GUI
or not
private ClientGUI
cg;
// the server, the
port and the username
private String
server, username;
private int port;
/*
* Constructor called by console mode
* server: the server address
* port: the port number
* username: the username
*/
Client(String
server, int port, String username) {
// which calls
the common constructor with the GUI set to null
this(server,
port, username, null);
}
/*
* Constructor call
when used from a GUI
* in console mode
the ClienGUI parameter is null
*/
Client(String
server, int port, String username, ClientGUI cg) {
this.server =
server;
this.port =
port;
this.username
= username;
// save if we
are in GUI mode or not
this.cg = cg;
}
/*
* To start the
dialog
*/
public boolean
start() {
// try to
connect to the server
try {
socket =
new Socket(server, port);
}
// if it
failed not much I can so
catch(Exception ec) {
display("Error connectiong to server:" + ec);
return
false;
}
String msg =
"Connection accepted " + socket.getInetAddress() + ":" +
socket.getPort();
display(msg);
/* Creating
both Data Stream */
try
{
sInput = new ObjectInputStream(socket.getInputStream());
sOutput =
new ObjectOutputStream(socket.getOutputStream());
}
catch
(IOException eIO) {
display("Exception creating new Input/output Streams: " +
eIO);
return false;
}
// creates the
Thread to listen from the server
new
ListenFromServer().start();
// Send our
username to the server this is the only message that we
// will send
as a String. All other messages will be ChatMessage objects
try
{
sOutput.writeObject(username);
}
catch
(IOException eIO) {
display("Exception doing login : " + eIO);
disconnect();
return
false;
}
// success we
inform the caller that it worked
return true;
}
/*
* To send a
message to the console or the GUI
*/
private void
display(String msg) {
if(cg == null)
System.out.println(msg); //
println in console mode
else
cg.append(msg + "\n");
// append to the ClientGUI JTextArea (or whatever)
}
/*
* To send a
message to the server
*/
void
sendMessage(ChatMessage msg) {
try {
sOutput.writeObject(msg);
}
catch(IOException e) {
display("Exception writing to server: " + e);
}
}
/*
* When something
goes wrong
* Close the
Input/Output streams and disconnect not much to do in the catch clause
*/
private void
disconnect() {
try {
if(sInput
!= null) sInput.close();
}
catch(Exception e) {} // not much else I can do
try {
if(sOutput
!= null) sOutput.close();
}
catch(Exception e) {} // not much else I can do
try{
if(socket
!= null) socket.close();
}
catch(Exception e) {} // not much else I can do
// inform the
GUI
if(cg != null)
cg.connectionFailed();
}
/*
* To start the
Client in console mode use one of the following command
* > java
Client
* > java
Client username
* > java
Client username portNumber
* > java
Client username portNumber serverAddress
* at the console
prompt
* If the
portNumber is not specified 1500 is used
* If the
serverAddress is not specified "localHost" is used
* If the username
is not specified "Anonymous" is used
* > java
Client
* is equivalent
to
* > java
Client Anonymous 1500 localhost
* are eqquivalent
*
* In console
mode, if an error occurs the program simply stops
* when a GUI id
used, the GUI is informed of the disconnection
*/
public static void
main(String[] args) {
// default
values
int portNumber
= 1500;
String
serverAddress = "localhost";
String
userName = "Anonymous";
// depending
of the number of arguments provided we fall through
switch(args.length) {
// >
javac Client username portNumber serverAddr
case 3:
serverAddress = args[2];
// >
javac Client username portNumber
case 2:
try {
portNumber = Integer.parseInt(args[1]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java Client [username]
[portNumber] [serverAddress]");
return;
}
// >
javac Client username
case 1:
userName = args[0];
// >
java Client
case 0:
break;
// invalid
number of arguments
default:
System.out.println("Usage is: > java Client [username]
[portNumber] {serverAddress]");
return;
}
// create the
Client object
Client client
= new Client(serverAddress, portNumber, userName);
// test if we
can start the connection to the Server
// if it
failed nothing we can do
if(!client.start())
return;
// wait for
messages from user
Scanner scan =
new Scanner(System.in);
// loop
forever for message from the user
while(true) {
System.out.print("> ");
// read
message from user
String msg
= scan.nextLine();
// logout
if message is LOGOUT
if(msg.equalsIgnoreCase("LOGOUT"))
{
client.sendMessage(new ChatMessage(ChatMessage.LOGOUT, ""));
//
break to do the disconnect
break;
}
// message
WhoIsIn
else
if(msg.equalsIgnoreCase("WHOISIN")) {
client.sendMessage(new ChatMessage(ChatMessage.WHOISIN,
""));
}
else
{ // default to ordinary
message
client.sendMessage(new ChatMessage(ChatMessage.MESSAGE, msg));
}
}
// done
disconnect
client.disconnect();
}
/*
* a class that
waits for the message from the server and append them to the JTextArea
* if we have a
GUI or simply System.out.println() it in console mode
*/
class
ListenFromServer extends Thread {
public void
run() {
while(true) {
try {
String msg = (String) sInput.readObject();
//
if console mode print the message and add back the prompt
if(cg == null) {
System.out.println(msg);
System.out.print(">
");
}
else {
cg.append(msg);
}
}
catch(IOException e) {
display("Server
has close the connection: " + e);
if(cg != null)
cg.connectionFailed();
break;
}
//
can't happen with a String object but need the catch anyhow
catch(ClassNotFoundException e2) {
}
}
}
}
}
No comments:
Post a Comment