Building a Multi-Client TCP Chat System in Java (JavaFX + Maven)
Overview
For our Advanced Programming Paradigms class project, we built a TCP chat system where multiple clients can connect to a server, choose a username, exchange messages in real time, and see a live list of connected users. The project uses Java sockets, multi-threading, JavaFX for the user interface, and Maven for dependency management and builds.
Requirements implemented
- Multi-client TCP server (each client handled concurrently)
- JavaFX Server UI (start/stop server, log, connected users)
- JavaFX Client UI (join, send messages, receive messages, user list)
- Configuration file for IP/Port (
config.properties) - Command-line arguments override configuration
- Clean shutdown (port is released properly after stopping)
Features
Server
- Accepts multiple clients simultaneously
- Broadcasts chat messages to all connected clients
- Sends informational events (user joined/left)
- Shows connected users in the Server UI
- Stops cleanly (no “address already in use” after stop)
Client
- Connects to server via TCP
- Sends and receives messages
- Shows chat messages + system messages
- Displays the list of connected users
- Reads default IP/port from config file
Architecture (multi-module Maven)
I organized the project as a multi-module Maven project:
-
TCPServermodule (server-side logic + server JavaFX UI) -
TCPClientmodule (client networking + client JavaFX UI)
This separation makes the code easier to understand and closer to real-world architecture.
Main server classes
-
ChatServer: owns theServerSocket, accepts connections, manages connected clients. -
ClientHandler: one instance per client; runs in its own thread. -
ServerListener: interface used to notify the UI about user list/log changes. -
ServerController: JavaFX controller; starts/stops the server and updates UI.
Main client classes
-
ChatClient: opens a socket, sends/receives lines, runs a background listener thread. -
ClientListener: interface used to push events/messages to the UI. -
ChatController: JavaFX controller; reacts to events and updates UI.
Communication protocol
Messages are sent as simple text lines using a small protocol, for example:
JOIN|usernameMSG|username|messageINFO|some textUSERS|user1,user2,user3
This approach keeps parsing simple and deterministic.
Key programming paradigms used
1) Concurrency (multi-threading)
The server accepts clients in a loop. For each connection, it spawns a new thread with a ClientHandler.
This allows multiple clients to chat at the same time.
2) Thread-safe collections
The server stores connected clients in a:
ConcurrentHashMap<String, ClientHandler>
This avoids race conditions when multiple client threads connect/disconnect at the same time.
3) Listener (Observer-style) pattern for UI updates
JavaFX UI updates must run on the UI thread.
To keep networking code independent from UI, I used interfaces:
-
ServerListener(server → server UI) -
ClientListener(client → client UI)
The UI controller implements the listener and updates JavaFX components safely (using Platform.runLater where needed).
Technical challenges and how I fixed them
Challenge 1: “Address already in use: bind”
We often got this error after restarting the server:
- it happens when port 3000 is still occupied by a running process
Root cause: the server socket wasn’t being closed properly, or a second server run was started accidentally.
Fix: store ServerSocket as a class field and close it in stopServer() so the port is released:
-
serverSocket.close()unblocksaccept()and stops the server loop - also removed/avoided duplicate run configurations that started another server on the same port
Challenge 2: Duplicate usernames and concurrency
Two clients could attempt to register the same username at almost the same time.
Fix: use atomic insertion:
-
clients.putIfAbsent(username, handler)If the username already exists, registration fails immediately without extra locking.
Challenge 3: JavaFX + networking thread safety
Networking runs on background threads, but JavaFX UI must update on the UI thread.
Fix: use listener callbacks and wrap UI updates with:
Platform.runLater(() -> { /* update UI */ })
Challenge 4: Configuration and runtime flexibility
Hardcoding localhost:3000 is not flexible.
Fix: load defaults from config.properties and allow override using main(String[] args):
- Client:
TCPClient <ip> <port> - Server:
TCPServer <port>
How to run
Option A — Run using IntelliJ Maven panel
1. Start Server:
- Maven →
TCPServer→ Plugins →javafx→javafx:run Click Start Server in the UI
2. Start Clients:Maven →
TCPClient→ Plugins →javafx→javafx:runRun multiple times for multiple clients
Option B — Run using IntelliJ “Application” configs (for command-line args)
Create a Run Configuration:
-
Server main class:
server.app.TCPServer- Program args:
4000
- Program args:
-
Client main class:
client.app.TCPClient- Program args:
127.0.0.1 4000
- Program args:
Final result
At the end, we had a working chat system where:
- The server UI shows logs + connected users
- Multiple clients can connect and chat concurrently
- Users list updates in real time
- The server stops cleanly and releases the port
What we learned
This project made me much more comfortable with:
- TCP sockets and simple protocols
- Designing concurrent systems safely
- Thread-safety and shared state (
ConcurrentHashMap, atomic registration) - Separating networking code from UI using listener patterns
- JavaFX thread rules (
Platform.runLater)
Repo : https://github.com/Abdell-EL/Chat_system
Author: Abdellah Elouazzani - Ilyass Bartal - Anouar El Yaagoubi
Top comments (0)