A multi-threaded client-server application demonstrating distributed computing, concurrent task processing, and real-time network communication in Java.
The server accepts multiple simultaneous TCP connections via a thread pool, processes text commands, and logs all activity with timestamps. The Swing-based client provides a dark-themed terminal-style GUI with asynchronous message handling and color-coded output.
| Feature | Description |
|---|---|
| Thread-Pool Concurrency | Fixed-size ExecutorService handles up to 10 simultaneous clients without spawning unbounded threads |
| Command Processing | 10 built-in commands: echo, upper, lower, reverse, count, length, replace, time, ping, help |
| Timestamped Logging | All connections, commands, and responses logged to server.log with yyyy-MM-dd HH:mm:ss timestamps |
| Graceful Shutdown | Shutdown hook cleanly terminates the thread pool and flushes logs on Ctrl-C |
| Client Tracking | Each client gets a unique ID for log traceability |
| Null-Safe Reads | Handles abrupt client disconnects without NullPointerException |
| Feature | Description |
|---|---|
| Dark Terminal UI | Monospaced dark-themed Swing interface with color-coded messages |
| Async Reader Thread | Background thread receives server messages without blocking the UI |
| Timestamped Messages | Sent and received messages display [HH:mm:ss] timestamps |
| Status Bar | Live connection status indicator at the bottom of the window |
| Clean Disconnect | Window close handler sends goodbye and releases all resources |
| EDT-Safe | All GUI operations run on the Event Dispatch Thread |
| Command | Example | Result |
|---|---|---|
echo <msg> |
echo hello |
Echo: hello |
upper <msg> |
upper hello |
HELLO |
lower <msg> |
lower HELLO |
hello |
reverse <msg> |
reverse hello |
olleh |
count <msg> |
count hello world |
Word count: 2 |
length <msg> |
length hello |
Length: 5 |
replace <old> <new> <msg> |
replace l r hello |
herro |
time |
time |
Server date and time |
ping |
ping |
pong |
help |
help |
Lists all commands |
bye |
bye |
Disconnects |
┌──────────────────┐ TCP :12345 ┌───────────────────┐
│ Client (Swing) │ ◄─────────────────────────► │ Server │
│ │ text commands / responses │ │
│ ┌────────────┐ │ │ ┌─────────────┐ │
│ │ GUI Thread │ │ │ │ Main Thread │ │
│ │ (EDT) │ │ │ │ accept() │ │
│ └────────────┘ │ │ └──────┬──────┘ │
│ ┌────────────┐ │ │ │ │
│ │ Reader │ │ │ ┌──────▼──────┐ │
│ │ Thread │ │ │ │ ThreadPool │ │
│ └────────────┘ │ │ │ (10 workers)│ │
└──────────────────┘ │ └──────┬──────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ClientHandler│ │
│ │ (per client)│ │
│ └──────┬──────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ server.log │ │
│ └─────────────┘ │
└───────────────────┘
RunnableoverThread—ClientHandlerimplementsRunnableand is submitted to anExecutorService, not subclassed fromThread.- Shared synchronized logger — A single
PrintWriterwithsynchronizedaccess prevents log interleaving from concurrent handlers. - Null-safe read loop —
while ((line = reader.readLine()) != null)handles abrupt disconnects without crashing. - Background reader thread — The client reads server responses on a dedicated thread, preventing GUI freezes on
readLine(). - EDT compliance — All Swing UI mutations happen on the Event Dispatch Thread via
SwingUtilities.invokeLater(). - Try-with-resources — Server uses try-with-resources on the socket, reader, and writer for guaranteed cleanup.
- Java 11+ (uses
var, text blocks,String.isBlank(),java.time)
git clone https://github.com/shuddha2021/Distributed-Java-App.git
cd Distributed-Java-AppTerminal 1 — Start the server:
javac src/Server.java -d out
java -cp out ServerTerminal 2 — Start a client:
javac src/Client.java -d out
java -cp out ClientYou can launch multiple client instances to test concurrent connections.
Once connected, type these commands in the client:
help
echo Hello, World!
upper distributed systems
reverse networking
count Java is awesome
time
ping
bye
Distributed-Java-App/
├── src/
│ ├── Server.java ← multi-threaded server with command processor and logging
│ └── Client.java ← Swing GUI client with async reader and dark theme
├── .gitignore ← ignores build artifacts, IDE files, and logs
└── README.md
| Concern | Implementation |
|---|---|
| Language | Java 11+ |
| Networking | java.net.ServerSocket, java.net.Socket — raw TCP sockets |
| Concurrency | ExecutorService with fixed thread pool, AtomicInteger for client IDs |
| GUI | Swing (JFrame, JTextPane, StyledDocument) with dark theme |
| Logging | Timestamped file logging via java.time.LocalDateTime |
| I/O | BufferedReader / PrintWriter with try-with-resources |
| Bug | Fix |
|---|---|
ServerThread extends Thread submitted to ExecutorService |
Changed to ClientHandler implements Runnable |
readLine() null not checked — NullPointerException on client disconnect |
while ((line = reader.readLine()) != null) loop |
Each thread opened its own FileWriter("server.log") — race condition |
Single shared PrintWriter with synchronized log method |
Socket, reader, writer not closed in finally |
Try-with-resources on all I/O resources |
Client created new BufferedReader on every sendCommand() |
Single reader on a background thread |
| GUI not built on EDT | SwingUtilities.invokeLater() in main() |
Debug System.out.println left in client |
Removed |
No ExecutorService shutdown |
Shutdown hook added |
count used .split(" ") (double spaces = empty tokens) |
Changed to .split("\\s+") |
Contributions welcome. Ideas for extension:
- SSL/TLS encrypted connections
- User authentication and session management
- Persistent command history
- File transfer protocol
- REST API alongside raw sockets
- Unit tests with JUnit
Fork the repo, create a feature branch, and submit a pull request.
MIT