Skip to content

HarshEvolves/multi-client-chat-application

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multi-Client TCP Chat Server

A high-concurrency, multithreaded chat application built in C using POSIX threads and TCP/IP sockets. This project demonstrates core systems programming concepts, including network communication, thread synchronization, and resource management in a Linux environment. It facilitates real-time, bidirectional communication between multiple remote clients and a central server, handling concurrent connections through an efficient multi-threaded architecture.

Features

  • Concurrent Client Handling: Supports multiple simultaneous client connections using multithreading.
  • Real-Time Message Broadcasting: Efficient message distribution to all active clients.
  • Thread-Safe State Management: Utilizes mutex locks to prevent race conditions during client list modifications.
  • Graceful Disconnection Handling: Detects client disconnects and notifies remaining users while cleaning up resources.
  • Custom Usernames: Allows clients to identify themselves with unique usernames upon connection.

Tech Stack

  • Language: C (C99/C11)
  • APIs: POSIX Threads (pthreads), Berkeley Sockets API
  • Environment: Linux / Unix-like OS

System Architecture

+-------------------+                          +-------------------+
|                   |      TCP Sockets         |                   |
|  Chat Client 1    | <======================> |                   |
|  (Send/Recv Thds) |                          |                   |
+-------------------+                          |                   |
                                               |                   |
+-------------------+                          |   Central Chat    |
|                   |      TCP Sockets         |   Server          |
|  Chat Client 2    | <======================> |   (Main Thread)   |
|  (Send/Recv Thds) |                          |                   |
+-------------------+                          |                   |
                                               |                   |
+-------------------+                          |                   |
|                   |      TCP Sockets         |   Client Threads  |
|  Chat Client N    | <======================> |   (T1, T2... TN)  |
|  (Send/Recv Thds) |                          |                   |
+-------------------+                          +-------------------+

Project Structure

.
├── chat_server.c       # Main server implementation and connection handling
├── chat_client.c       # Client application with send/receive threads
├── server              # Compiled server binary
└── client              # Compiled client binary

Working Flow

  1. Server Initialization: The server initializes a TCP socket, binds it to a specific port (8080), and begins listening for incoming connections.
  2. Client Connection: A client connects to the server's IP address and port, prompting the user for a username.
  3. Thread Dispatch: Upon accepting a client connection, the server spawns a dedicated POSIX thread to handle I/O for that specific client, allowing the main thread to continue accepting new connections.
  4. Client Threading: The client spawns two threads: one dedicated to reading user input from stdin and sending it to the server, and another dedicated to receiving broadcasts from the server and printing them to stdout.
  5. Message Broadcasting: When a client thread on the server receives a message, it locks the shared client list using a mutex, iterates through all connected sockets, and transmits the message to everyone except the sender.
  6. Disconnection: If a client drops or types exit, the server thread removes the socket from the active list (in a thread-safe manner), broadcasts a departure message, and terminates gracefully.

Core Concepts Demonstrated

Operating Systems

  • Multithreading: Designing a concurrent system where multiple threads of execution run within a single process address space.
  • Resource Management: Proper allocation and deallocation of file descriptors (sockets) and heap memory (thread arguments) to prevent memory and resource leaks.

Computer Networks

  • TCP/IP Protocol Suite: Utilizing the Transmission Control Protocol for reliable, stream-oriented, connection-based data delivery.
  • Client-Server Model: Implementing a centralized architecture where clients request services (message routing) from a central server.
  • Berkeley Sockets: Using the standard API for network I/O across UNIX-like systems.

Concurrency

  • Race Conditions: Identifying critical sections in the code where shared memory is accessed by multiple threads simultaneously.
  • Mutual Exclusion: Employing mutexes to serialize access to shared resources (the array of connected client file descriptors).

Important System Calls / APIs Used

Function Purpose Why it is used
socket() Networking Creates a network endpoint for communication. Used with AF_INET and SOCK_STREAM to specify IPv4 TCP connections.
bind() Networking Associates the server socket with a specific network interface and port number, allowing clients to locate the service.
listen() Networking Marks the socket as passive, indicating it will accept incoming connection requests, and defines the backlog queue size.
accept() Networking Blocks the server's main thread until a connection request arrives, returning a new file descriptor specifically for communicating with that client.
connect() Networking Initiates a TCP handshake from the client to the server's specified IP address and port.
send() Networking Transmits data (messages or usernames) over the established TCP connection.
recv() Networking Reads incoming data from the socket. A return value of 0 or less reliably indicates client disconnection or network failure.
close() OS/Networking Releases the file descriptor back to the operating system, terminating the TCP connection and freeing resources.
pthread_create() Concurrency Spawns a new thread. Used by the server to handle each client asynchronously, and by the client to separate reading and writing operations.
pthread_join() Concurrency Blocks the calling thread until the specified thread terminates. Used in the client to ensure the main program doesn't exit prematurely.
pthread_mutex_lock() Concurrency Acquires a lock before entering a critical section (reading/writing the client list). Blocks if another thread holds the lock.
pthread_mutex_unlock() Concurrency Releases the lock after leaving a critical section, allowing other waiting threads to proceed.

Thread Synchronization Strategy

The server maintains a global array connectedClients and an integer totalClients. Because multiple client threads can simultaneously attempt to broadcast messages (reading the array) or disconnect (modifying the array), this data structure is a critical section. A single pthread_mutex_t clientLock is used to enforce mutual exclusion. Whenever a thread needs to iterate through the list to send messages, add a new client, or remove a disconnected client, it must first acquire clientLock. This guarantees state consistency and prevents segmentation faults or broadcasting to invalid file descriptors.

How Message Broadcasting Works

When the server receives a message from a client, the thread handling that client locks the mutex and iterates through the connectedClients array. It compares each socket descriptor against the sender's socket descriptor. If they do not match, the thread calls send() to push the message payload to that client's socket buffer. Once the iteration is complete, the mutex is unlocked.

Error Handling

  • System Call Validation: Return values of all critical network system calls (socket, bind, listen, accept, connect) are checked. If an error occurs (return value < 0), perror() is used to print the exact system error, followed by program termination or graceful loop continuation.
  • Connection Loss Detection: The recv() function returning 0 (graceful shutdown) or -1 (error) is used to detect disconnected clients, triggering local cleanup procedures without crashing the server.
  • Capacity Management: The server enforces a hard limit MAX_USERS. Incoming connections beyond this limit are accepted, immediately sent a rejection notice, and closed, preventing buffer overflow on the connectedClients array.

Build Instructions

To compile the project, you need gcc installed on your system. The project links against the pthread library.

# Compile the Server
gcc chat_server.c -o server -lpthread

# Compile the Client
gcc chat_client.c -o client -lpthread

Running the Project

  1. Start the Server: Run the server binary on the host machine. It will automatically bind to port 8080.

    ./server
  2. Start the Clients: Open new terminal windows and run the client binary, passing the server's IP address.

    ./client 127.0.0.1

Example Output

Server Terminal:

Chat Server started on port 8080...
Alice connected.
Bob connected.
Alice disconnected.

Client Terminal (Alice):

Enter your username: Alice
Connected to Chat Server as 'Alice'.
Type your message and press Enter to send. Type 'exit' to disconnect.
Bob joined the chat.
Bob: Hello everyone!
exit
You have disconnected.

Limitations

  • Plaintext Communication: All messages are transmitted in plaintext over TCP, making them vulnerable to packet sniffing.
  • Fixed User Limit: The server can only handle a hardcoded maximum number of clients (MAX_USERS = 10), determined at compile time.
  • No Persistence: Chat history is not saved; new users cannot see messages sent prior to their connection.
  • Scalability: Thread-per-client model is resource-intensive and does not scale well to thousands of connections compared to event-driven models (e.g., epoll or select).

Future Improvements

  • Implement epoll() or select() for asynchronous I/O to replace the thread-per-client model for better scalability.
  • Add SSL/TLS via OpenSSL to encrypt chat traffic.
  • Implement a chat history feature using an in-memory database like Redis or a relational database like SQLite.
  • Support private messaging between specific clients.

Learning Outcomes

  • Deepened understanding of TCP/IP socket lifecycle and network programming in C.
  • Gained practical experience with POSIX threads, managing concurrency, and understanding the challenges of multi-threaded programming.
  • Mastered synchronization primitives, specifically mutexes, to prevent race conditions.
  • Improved ability to design and implement robust client-server architectures at a low level.

About

Real-time multi-client chat application in C using TCP/IP sockets and POSIX threads.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages