The basic idea for the server is as follow:
ChatServer.java
- Listen on a port until a client connects using the SocketServer.accept()
- Once, connected, create a ChatService and have it handle that connection in a separate thread
- Now, go back to listening mode again for next connection.
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Basic Chat Server (SimpleIRC)
* @author TA Nguyen
*/
public class ChatServer
{
// Defines the port that this server will listen on
final int SBAP_PORT = 80;
public static void main(String[] args) throws IOException
{
// Create a new mediator
Mediator mediator = new Mediator();
// Create a new server object
ServerSocket server = new ServerSocket(SBAP_PORT);
System.out.println("");
System.out.println("Press CTRL+C to terminate.");
System.out.println("Waiting for clients to connect...");
/**
* Main loop of the application.
*/
while (true) {
// wait for client to connect
Socket socket = server.accept();
System.out.println("Client connected.");
// ok, connected,
//now create ChatService to handle this connection
ChatService service = new ChatService(socket, mediator);
// tell the JVM to run it in a thread
Thread thread = new Thread(service);
thread.start();
// now, we go back to wait for next connection.
}
}
}
ChatService.java
- Listen for incoming message and tell the Mediator to send it to all connecting clients
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* ChatService: Executes BChat commands from a socket.
* @author TA Nguyen
*/
public class ChatService implements Runnable
{
private Socket socket;
private Scanner in;
private Mediator mediator;
private PrintWriter writer;
/**
* Constructs a service object that processes commands
* from a socket utilizing the mediator service.
* @param socket the Socket
* @param mediator the Mediator
*/
public ChatService(Socket socket, Mediator mediator) {
this.socket = socket;
mediator = mediator;
}
/**
* Add new i/o services and call doService()
* which performs the primary chat functions.
*
* When doService() returns, cleans up and shuts down the thread.
*/
public void run() {
try {
try {
in = new Scanner(socket.getInputStream());
writer = new PrintWriter(socket.getOutputStream());
mediator.add(writer);
doService();
} finally {
mediator.remove(writer);
socket.close();
System.out.println("Client disconnected.");
}
} catch (IOException exception) {
System.out.println("Serverside error occured.");
exception.printStackTrace();
}
}
/**
* Prints every command received. If the user enters "QUIT",
* then the thread terminates.
*/
public void doService() throws IOException {
while (true) {
if (!in.hasNext()) {
return;
}
String command = in.nextLine();
if (command.equals("QUIT")) {
return;
} else {
executeCommand(command);
}
}
}
/**
* Executes a single command.
* @param command the command to execute
*/
public void executeCommand(String command) {
mediator.writeMessage(command);
}
}
Mediator.java
- keep track of all the outgoing socket (clients)
- loop and display received a message to all clients
import java.io.PrintWriter;
import java.util.LinkedList;
/**
* Mediator:
* 1. Stores and maintains a list of the clients
* that are connected to the server.
* 2. Provide a relay of received messages
* to all connected clients.
*
* This list must be synchronized across all threads
* to maintain access to each connection.
*
* @author TA Nguyen
*/
public class Mediator {
private LinkedList connections;
/**
* Constructor creates the list that maintains the connections
*/
public Mediator() {
connections = new LinkedList();
}
/**
* Accepts a message and sends it out to all of the clients
* @param Message the message to broadcast to all connections
*/
public void writeMessage(String message) {
System.out.println("Recieved: " + message);
for (PrintWriter out : connections) {
out.println(message);
out.flush();
}
}
/**
* Adds a new connection
* @param Out the new connection to add to the list
*/
public void add(PrintWriter out) {
connections.add(out);
}
/**
* Removes a connection
* @param Out the connection to remove
*/
public void remove(PrintWriter out) {
connections.remove(out);
}
}
ChatClient.java
- Establish a connection to the ChatServer
- Create a DisplayService and run it in a thread to handle server income message
- Loop and wait for a user to enter a message, then send it to the server.
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* Basic Chat Client (BChat)
*
* @author TA Nguyen
*/
public class ChatClient
{
// The port to connect to
private static final int BCHAT_PORT = 80;
private static final String BCHAT_IP = "localhost";
public static void main(String[] args) throws IOException {
// Create the socket and connect to the predefined server:port
Socket socket = new Socket(WTSCS_IP, WTSCS_PORT);
// Create the input and output streams, from this socket connection
InputStream instream = socket.getInputStream();
OutputStream outstream = socket.getOutputStream();
// Create a scanner and print writer for local i/o
Scanner socketIn = new Scanner(instream);
PrintWriter socketOut = new PrintWriter(outstream);
Scanner keyIn = new Scanner(System.in);
// Define the display service and instruct it to listen
// to the socket input stream, display all messages the come in.
DisplayService service = new DisplayService(socketIn);
// Well, do it (display) in a thread so that
// we can type without being blocked by incoming messages.
Thread thread = new Thread(service);
thread.start();
// first, get the client name and remember it.
System.out.print("Enter your name: ");
String name = keyIn.nextLine();
while (true) {
// read a line of message from (system/console) input stream
String line = keyIn.nextLine();
// write the message with client name to the socket output stream
socketOut.println(name + ": " + line);
socketOut.flush();
// if the last message is "QUIT" then stop.
if ("QUIT".equals(line)) {
break;
}
}
socket.close();
}
}
DisplayService.java
- Wait for a message from the server and display it to the console.
import java.util.Scanner;
/**
* DisplayService:
* Displays all input received.
*
* @author TA Nguyen
*/
public class DisplayService implements Runnable {
private Scanner scanner;
/**
* Constructor receives a scanner object for i/o operations.
*
* @param scanner The source to read input from
*/
public DisplayService(Scanner scanner) {
this.scanner = scanner;
}
/**
* Get input from the scanner and print the line to the output.
*/
public void run() {
while (true) {
if(scanner.hasNext()) {
String line = scanner.nextLine();
System.out.println(line);
} else {
return;
}
}
}
}
Now, that you have the basic understanding of client/server using the socket and a working chat client/server, I challenge you to take this to a step further and add a GUI to the client, along with the Avatar image for the client. Hint you can use the java serialization to send complete Java object over the socket.
Note: this demo was originally written as part of the class exercise for CSC-251 class that I teach at WakeTech.edu
Enjoy,
T.A. Nguyen
No comments:
Post a Comment