The goal of this lab is to build a multi-client chat application using Java sockets and multithreading to handle concurrent connections.
In modern networked applications, the ability to handle multiple simultaneous clients is essential. From chat applications and online gaming to distributed systems, the client-server architecture using socket programming forms the backbone of network communication.
In this lab, we will implement a robust multi-client chat server that can efficiently handle multiple concurrent connections.
Objectives
In this lab you will:
Implement a multi-threaded server that:
Listens for incoming client connections
Creates a new thread for each client
Broadcasts messages to all connected clients
Handles client disconnections gracefully
Implement a chat client that:
Connects to the server
Sends messages to the server
Receives and displays messages from other clients
Handles connection errors and server disconnection
Understand and implement proper resource management:
Closing sockets and streams when they are no longer needed
Handling exceptions appropriately
Implementing a clean shutdown mechanism
Requirements and Tools
Java JDK 11 or above
Basic understanding of TCP/IP networking
Understanding of Java threads and concurrency
Knowledge of Java I/O streams
Problem Statement
You are building a simple chat application that allows multiple users to connect and exchange messages in a common chatroom. The server should be able to handle connections from multiple clients simultaneously and broadcast messages to all connected clients. The client should be able to send messages to the server and display messages received from other clients.
Getting Started
If your instructor is using GitHub classroom, you will need to accept the assignment using the link at the bottom of this page, clone your auto-generated repository, and import it as a project into your IDE.
In this part, you will implement the server-side components of the chat application.
Task 1.1: Create a Message Class
First, create a ChatMessage class to represent the messages exchanged between clients and the server. This class will handle both plain text messages and special command messages, such as connect, disconnect, and list users. The class should keep track of the sender, timestamp, content, and the message type (connect, disconnect, list users, and message.)
This class should be serializable so that it can be easily transmitted over the network.
Complete the ChatMessage class at src/main/java/cpit305/fcit/kau/edu/sa/sockets/ChatMessage.java.
Next, implement a ClientHandler class that will manage communication with each connected client. This class should run in a separate thread for each client by either extending Thread or implementing the Runnable interface.
Complete the ClientHandler class at src/main/java/cpit305/fcit/kau/edu/sa/sockets/ClientHandler.java.
publicclassClientHandlerimplements Runnable {
privatefinal Socket clientSocket;
privatefinal ChatServer server;
private ObjectInputStream inputStream;
private ObjectOutputStream outputStream;
private String clientName;
privateboolean isRunning;
publicClientHandler(Socket socket, ChatServer server) {
this.clientSocket= socket;
this.server= server;
this.isRunning=true;
}
@Override
publicvoidrun() {
try {
// Initialize I/O streams// We should create the output stream before the input stream to avoid deadlocks outputStream =new ObjectOutputStream(clientSocket.getOutputStream());
inputStream =new ObjectInputStream(clientSocket.getInputStream());
// Get client's name from the initial connection message (type CONNECT) ChatMessage chatMessage = (ChatMessage) inputStream.readObject();
if (chatMessage.getType() == ChatMessage.MessageType.CONNECT) {
this.clientName= chatMessage.getSender();
server.broadcastMessage(chatMessage, this);
}
// Handle messages in a loopwhile (isRunning){
ChatMessage message = (ChatMessage) inputStream.readObject();
switch (message.getType()) {
case ChatMessage.MessageType.DISCONNECT:
isRunning =false;
server.broadcastMessage(message, this)
break;
case ChatMessage.MessageType.USER_LIST:
// list all users and send it to this client using sendMessage()break;
case ChatMessage.MessageType.MESSAGE:
// ask the server to broadcast the message to all connected clientsbreak;
}
}
} catch (IOException | ClassNotFoundException e) {
System.err.println("Error with client connection: "+ e.getMessage());
} finally {
// Clean up resources when the client disconnects closeConnection();
}
}
publicvoidsendMessage(ChatMessage message) {
// Send a message to this client using its OutputStream }
privatevoidcloseConnection() {
// Close all streams and socket, notify server about disconnection }
public String getClientName() {
return clientName;
}
}
Task 1.3: Implement the Chat Server
Next, implement the ChatServer class that will listen for client connections and manage a list of connected clients.
Implement the ChatServer class at src/main/java/cpit305/fcit/kau/edu/sa/sockets/ChatServer.java.
If your instructor is using GitHub classroom, then you should click on your class submission link,
link your GitHub username to your name if you have not already done so, accept the assignment, clone the
repository into your local
development environment, and push the code to the remote repository on GitHub. Please make sure that your
written
answers are included in either a README (Markdown) file or a PDF file.
Lab dues dates are listed on GitHub classroom unless otherwise
noted.
If your instructor is using GitHub classroom, your submission will be
auto-graded
by running the included unit tests as well as manually graded for correctness, style, and quality.
How to submit your lab to GitHub Classroom
The video below demonstrates how to submit your work to GitHub classroom