This project is a multi-threaded web server implemented in C, developed as a practical assignment for an Operating Systems course. The server is built upon a producer-consumer model, utilizing a thread pool to handle multiple client requests concurrently.
The initial codebase was a simple, single-threaded web server. The main goal of this project was to refactor it to handle concurrent connections efficiently. This was achieved by implementing a master thread (producer) that accepts new connections and places them into a shared buffer, and a pool of worker threads (consumers) that retrieve requests from this buffer and process them.
Synchronization between threads is managed using mutexes and condition variables to prevent race conditions and ensure thread safety.
- Multi-threading: A pool of worker threads is created at startup to handle requests.
 - Bounded Buffer: A fixed-size shared buffer is used to hold incoming requests, decoupling the master thread from the worker threads.
 - Producer-Consumer Model: The master thread acts as the producer, and worker threads act as consumers.
 - Synchronization: Thread-safe access to the shared buffer is guaranteed through the use of 
pthread_mutex_tandpthread_cond_t, with no busy-waiting. - Scheduling Policies: The server supports two different scheduling policies for workers:
- FIFO (First-In-First-Out): The oldest request in the buffer is served first.
 - SFF (Smallest File First): The request for the smallest file (in bytes) is served first to improve response time for short requests.
 
 
The project includes a Makefile that simplifies the compilation process. To compile all necessary files, simply run:
makeThis will generate three main executables:
wserver: The concurrent web server.wclient: A simple web client for testing.spin.cgi: A CGI script used to simulate long-running requests.
To clean up compiled files, you can run:
make cleanThe server can be configured via command-line arguments.
./wserver [-d basedir] [-p port] [-t threads] [-b buffers] [-s schedalg]
-d basedir: Specifies the root directory for the server's files. (Default: current directory)-p port: Specifies the port number to listen on. (Default: 10000)-t threads: The number of worker threads in the pool. (Default: 1)-b buffers: The number of slots in the request buffer. (Default: 1)-s schedalg: The scheduling algorithm to use. Options areFIFOorSFF. (Default: FIFO)
To run the server on port 8080 with 8 worker threads, a buffer of 16 slots, and the SFF scheduling policy:
./wserver -p 8080 -t 8 -b 16 -s SFF- Run the server in one terminal window.
 - Open new terminals to run the client(s).
 
Create a test file:
echo "<h1>Hello, World!</h1>" > index.htmlRequest the file using wclient:
./wclient localhost 10000 /index.htmlTo see the concurrency and scheduling policies in action, you will need the spin.cgi script. First, make sure it is executable by running this command once:
chmod +x spin.cgiThe goal is to show that SFF will serve a short request before a long one, even if the long one arrived first in the queue.
- Start the server with multiple threads in SFF mode: 
./wserver -t 2 -s SFF - Create and run a test script (
test_sff.sh) to send a "traffic jam" of requests:#!/bin/bash ./wclient localhost 10000 "/spin.cgi?5" & ./wclient localhost 10000 "/spin.cgi?5" & ./wclient localhost 10000 /index.html & ./wclient localhost 10000 "/spin.cgi?4" & wait
 - Observe the server's output to confirm that 
index.htmlis chosen from the buffer before the otherspinrequests that are waiting. 
The goal is to show that a long request will block a subsequent short request when no threads are available.
- Start the server with only one thread in FIFO mode: 
./wserver -t 1 -s FIFO - In a new terminal (Terminal A), send a long-running request:
./wclient localhost 10000 "/spin.cgi?5" - Immediately after, in another new terminal (Terminal B), send a short request:
./wclient localhost 10000 /index.html
 - You will see that the request in Terminal B waits for the first request to complete before it is served.