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