Anthony Bartlett - 0255 6059 Denzil Erza-Essien - 0259 3040
- High-level summary
- Server implementation (
chat_server.c) - Client implementation (
chat_client.c) - Build & Run (exact flags used)
- Supported Commands
- Repository layout
- Conclusion & Future Improvements
- The server is UDP-based, listens on
SERVER_PORT, stores connected clients in a linked list, and spawns a detached worker thread per incoming request. - The client is a terminal UI implemented with
ncurses, spawns one sender thread and one listener thread, and sends raw requests liketype$ payloadto the server. - Two proposed extensions are implemented:
- PE1: A circular history buffer of the last 15 broadcast messages; history is sent to new clients on
conn$. - PE2: An inactivity monitor thread periodically pings the least-recently-active client and removes it if it does not respond.
- PE1: A circular history buffer of the last 15 broadcast messages; history is sent to new clients on
- Type:
Clientstruct holds:activeflagname(string)addr(struct sockaddr_in)mutedlist (array of strings) andmuted_countlast_activetimestamp (time_t)ping_sentandping_timefor inactivity handling
- Each client is wrapped in a
ClientNodeand stored in a dynamically allocated singly linked listtypedef struct ClientNode { Client client; struct ClientNode *next; } ClientNode;
- The head pointer
static ClientNode *clients_head;represents the start of the client list - A linked list is used to allow dynamic growth of connected clients without imposing a fixed maximum client limit in code. Clients can be added and removed without shifting memory, which simplifies implementation for insertion and deletion.
- Main listener loop:
- Calls
udp_socket_read(sd, &client_addr, buf, BUFFER_SIZE)to receive a datagram. - For every valid incoming datagram, it allocates a
worker_arg_t, copies the request and the client's address, and spawns a detached thread that runsrequest_handler.
- Calls
request_handler:- Parses messages of form
type$payload(looks for$delimiter). - Recognized types:
conn,say,sayto,mute,unmute,rename,disconn,kick, andret-ping(the ping reply handling is minimal). - For malformed or unknown commands it replies with an
ERR$-prefixed message.
- Parses messages of form
broadcast_all(sd, msg, skip):- Writes the message to history then iterates active clients and uses
udp_socket_writeto send to each (skipsskip_idxwhen provided).
- Writes the message to history then iterates active clients and uses
broadcast_from_sender(sd, sender, msg):- Respects recipients' mute lists by calling
recipient_has_muted_sender. - Does not deliver a sender's message to themselves.
- Respects recipients' mute lists by calling
- Private messaging (
sayto):- The server parses the first token of
payloadas recipient name, finds recipient on the linked list viafind_client_by_name, checks if recipient muted the sender, then sends a direct UDP message to the recipient and aSYS$ack to the sender.
- The server parses the first token of
- Circular buffer implemented with:
history[HISTORY_SIZE][BUFFER_SIZE]history_start,history_count- Protected by
history_lock(a pthread mutex).
- On every broadcast,
history_add(msg)appends to the circular buffer. - On successful
conn$the server callshistory_send_to_client(sd, &client_addr)to send each stored message prefixed with[History]so the client can detect it and display accordingly.
- Monitor thread (
monitor_thread) runs periodically (intervalMONITOR_INTERVALseconds). - It finds the least-recently-active client by scanning the linked list under a read lock.
- If inactivity exceeds
INACTIVITY_THRESHOLD, it:- Sends a
ping$message to the selected client and marksping_sentandping_time. - If a previous ping exists and
PING_TIMEOUTelapses without activity (clients updatelast_activewhen they send any request), the monitor removes the client and broadcasts a disconnection message.
- Sends a
- Ping replies: clients reply with
ret-ping$. - On receiving
ret-ping, the server immediately updates:last_activeping_sent = 0ping_time = 0This prevents the client from being removed by the inactivity monitor thread.
clients_lockis apthread_rwlock_tprotecting the linked list:- Read lock (
pthread_rwlock_rdlock) used for operations that only inspect the linked list (e.g., scanning for recipients forsayto, broadcasting). - Write lock (
pthread_rwlock_wrlock) used for modifications:add_client,remove_client, muting/unmuting, rename, updatinglast_active, and marking ping fields.
- Read lock (
history_lockis apthread_mutex_tguarding the circular history buffer.- Rationale: Reader–writer lock allows multiple concurrent reads (e.g., many broadcast/send ops) while serializing updates.
- Admin client detection: server checks requester UDP port (
ntohs(client_addr.sin_port)) and only acceptskick$from port6666. - Upon
kick$ name, server:- Finds target by name.
- Sends
SYS$You have been removed from the chatto the target. - Removes client from linked list and broadcasts
SYS$<name> has been removed...to others.
- Uses
ncursesfor terminal UI:chat_pad: large pad used as the scrollable message area (created withnewpad(5000, cols)).input_win: single-line input window at the bottom for user typing.
- Threads:
listener_thread(int *sd):- Blocks on
udp_socket_readto receive server messages. - Adds each incoming line to
chat_padand stamps a local timestamp. - Recognizes messages prefixed with
[History]and handles them appropriately (keeps them in the pad like normal messages).
- Blocks on
sender_thread(struct sender_args *args):- Reads user input using
wgetchoninput_winand builds a request string. - Sends the raw request string to server with
udp_socket_write. - Special behavior: when user types
disconn$the sender setsshould_exit = 1and returns, letting the program terminate.
- Reads user input using
- Keyboard handling:
- Supports up/down keys to scroll chat pad (
KEY_UP,KEY_DOWN). - Handles backspace and line editing roughly (manual handling in loop).
- Supports up/down keys to scroll chat pad (
- Cursor and refresh logic ensure the pad and input area don't overlap.
- On listening, when a message begins with "[History]", the client strips that marker for display (server prefixes history with
[History]when sending to newly connected clients). - Timestamps are added on the client side (the client formats a local time
[HH:MM]and prints it right-aligned on the pad).
- By default the client binds to
CLIENT_PORTdefined as55555in the source. - Admin mode: if launched with
--adminargument (./chat_client --admin) the client binds to port6666. This allows sendingkick$commands accepted by the server as admin requests.
- The server uses pthreads; the client uses
ncurses. Build commands:
# From repository root (multithread-chat-app/)
gcc chat_server.c -o chat_server
gcc chat_client.c -o chat_client -lncurses -lpthread- Run server (foreground):
./chat_server- Or background:
./chat_server &
# kill when done:
pkill chat_server- Run client (normal user):
./chat_client- Run client (admin port 6666) to issue
kick$:
./chat_client --admin| Command | Description |
|---|---|
| conn$ NAME | Connect to the chat with a username |
| say$ MESSAGE | Broadcast message |
| sayto$ USER MESSAGE | Private message |
| mute$ USER | Mute user |
| unmute$ USER | Unmute user |
| rename$ NEWNAME | Change username |
| disconn$ | Disconnect |
| kick$ USER | Admin command (port 6666 only) |
| ret-ping$ | Client heartbeat reply |
chat_server.c— server (listener, worker threads, monitor thread, client table, history)chat_client.c— client (ncurses UI, sender/listener threads)udp.h— UDP helper wrappers and constants (used by both client and server)compile.sh— compilation scripttest_scripts/— automated tests to show functionality
This project demonstrates a complete multithreaded UDP-based chat application with support for both public and private messaging, mute functionality, administration controls, history replay, and inactivity detection. The system was designed with concurrency and robustness in mind, using POSIX threads, synchronisation primitives, and careful message handling.
The use of a linked list for client storage allows the server to scale dynamically. The separation between a listener thread, worker threads, and a monitor thread ensures responsive behaviour under concurrency and makes the program modular and maintainable.
Overall, the project meets its design goals and demonstrates effective use of networking, multithreading, and data structures in C.
Several enhancements could be made if the project were extended further:
Protocol & Networking
- Convert from UDP to TCP for guaranteed delivery and packet ordering.
Scalability & Performance
- Replace linear linked-list searching with a hash table for faster name and address lookup.
- Add thread pooling instead of spawning unbounded worker threads.
- Introduce rate limiting or flood protection to prevent abuse.
Security
- Add username authentication or password protection.
- Restrict admin commands via authentication rather than by port number.
Features
- Implement user-defined status messages (e.g., "away", "busy").
- Add chat room support.
UI Improvements
- Improve terminal layout handling under resize events.
- Highlight private messages or admin messages visually.
- Add a sidebar showing online users.
These changes would further improve robustness, usability, and scalability, while keeping the existing architecture as a strong foundation.