From 2cea565e6db7ed0ce02755d1fd20c338267fbff2 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Mon, 25 May 2026 22:05:17 +0200 Subject: [PATCH 1/9] Inital commit for the lab7, added .gitignore --- lab7/.gitignore | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lab7/.gitignore diff --git a/lab7/.gitignore b/lab7/.gitignore new file mode 100644 index 0000000..d727365 --- /dev/null +++ b/lab7/.gitignore @@ -0,0 +1,21 @@ +# --- Build Artifacts --- +bin/ +obj/ +*.o +*.out + +# --- Debug & Temporary Files --- +*.gch +.core +vgcore.* + +# --- IDEs and Editors --- +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# --- OS specific --- +.DS_Store +Thumbs.db \ No newline at end of file From 8505dcaa7f58df9079cb35bc9f7486af1d90beaa Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Mon, 25 May 2026 22:08:56 +0200 Subject: [PATCH 2/9] Created a Makefile for this project, added placeholder main.c files for client and server for testing it --- lab7/Makefile | 61 ++++++++++++++++++++++++++++++++++++++++++ lab7/src/client/main.c | 4 +++ lab7/src/server/main.c | 4 +++ 3 files changed, 69 insertions(+) create mode 100644 lab7/Makefile create mode 100644 lab7/src/client/main.c create mode 100644 lab7/src/server/main.c diff --git a/lab7/Makefile b/lab7/Makefile new file mode 100644 index 0000000..3a0e147 --- /dev/null +++ b/lab7/Makefile @@ -0,0 +1,61 @@ +# ============================================================================== +# Compiler Configuration +# ============================================================================== +CC = gcc +# -Wall -Wextra: Enable almost all compiler warnings +# -Werror: Treat all warnings as errors (strict code quality control) +# -g: Generate debugging symbols for GDB and Valgrind +# -std=c11: Use the ISO C11 standard +# -Iinclude: Look for header files inside the 'include' directory +CFLAGS = -Wall -Wextra -Werror -g -std=c11 -Iinclude + +# ============================================================================== +# Directory Structure +# ============================================================================== +SRC_DIR = src +OBJ_DIR = obj +BIN_DIR = bin + +# ============================================================================== +# Targets (Executables) +# ============================================================================== +SERVER_TARGET = $(BIN_DIR)/server +CLIENT_TARGET = $(BIN_DIR)/client + +# ============================================================================== +# Source and Object Files Mapping +# ============================================================================== +SERVER_SRCS = $(SRC_DIR)/server/main.c +CLIENT_SRCS = $(SRC_DIR)/client/main.c + +SERVER_OBJS = $(OBJ_DIR)/server/main.o +CLIENT_OBJS = $(OBJ_DIR)/client/main.o + +# ============================================================================== +# Build Rules +# ============================================================================== +.PHONY: all clean + +# Default rule to build both the server and the client +all: $(SERVER_TARGET) $(CLIENT_TARGET) + +# Rule to link the server executable +$(SERVER_TARGET): $(SERVER_OBJS) + @mkdir -p $(BIN_DIR) + $(CC) $(CFLAGS) $(SERVER_OBJS) -o $(SERVER_TARGET) + +# Rule to link the client executable +$(CLIENT_TARGET): $(CLIENT_OBJS) + @mkdir -p $(BIN_DIR) + $(CC) $(CFLAGS) $(CLIENT_OBJS) -o $(CLIENT_TARGET) + +# Generic pattern rule to compile source files into object files +# Automatically creates the required subdirectories inside obj/ +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +# Rule to clean up all build artifacts +clean: + @echo "Cleaning up build artifacts..." + rm -rf $(OBJ_DIR) $(BIN_DIR) \ No newline at end of file diff --git a/lab7/src/client/main.c b/lab7/src/client/main.c new file mode 100644 index 0000000..00583a8 --- /dev/null +++ b/lab7/src/client/main.c @@ -0,0 +1,4 @@ +// Minimal placeholder for the client application +int main(void) { + return 0; +} \ No newline at end of file diff --git a/lab7/src/server/main.c b/lab7/src/server/main.c new file mode 100644 index 0000000..6ac5606 --- /dev/null +++ b/lab7/src/server/main.c @@ -0,0 +1,4 @@ +// Minimal placeholder for the server application +int main(void) { + return 0; +} \ No newline at end of file From 10f7563973f46166035f9491916a09d7758ed478 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 13:29:15 +0200 Subject: [PATCH 3/9] Implemented the basic socket config in server.c --- lab7/src/server/main.c | 88 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/lab7/src/server/main.c b/lab7/src/server/main.c index 6ac5606..a73005e 100644 --- a/lab7/src/server/main.c +++ b/lab7/src/server/main.c @@ -1,4 +1,90 @@ -// Minimal placeholder for the server application +#include +#include +#include +#include +#include +#include + +/* Define the port on which the server will listen */ +#define SERVER_PORT 9000 + +/* * Defines the maximum number of pending connections in the queue. + * These are connections that have completed the TCP handshake but + * have not been accepted by the application yet. + */ +#define BACKLOG_SIZE 10 + int main(void) { + int server_fd; + struct sockaddr_in server_address; + int opt_reuse = 1; + + /* * STEP 1: Create the socket descriptor. + * - AF_INET: Specifies the IPv4 protocol family. + * - SOCK_STREAM: Specifies sequential, reliable TCP byte stream. + * - SOCK_CLOEXEC: Modern best practice. Automatically closes this file + * descriptor if the process executes another program via execve(). + */ + server_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (server_fd < 0) { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + /* * STEP 2: Configure socket options. + * - SOL_SOCKET: Specifies that the option is at the socket API level. + * - SO_REUSEADDR: Allows the socket to bind to an address/port that is + * in the TIME_WAIT state. Prevents the "Address already in use" error + * when restarting the server during debugging. + */ + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt_reuse, sizeof(opt_reuse)) < 0) { + perror("Setting SO_REUSEADDR failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + /* * STEP 3: Prepare the server address structure. + * - memset: Zeroes out the memory to prevent garbage data. + * - sin_family: Must match the domain specified in socket(). + * - sin_addr.s_addr: htonl(INADDR_ANY) configures the server to listen + * on ALL available network interfaces (WiFi, Ethernet, Localhost). + * - sin_port: htons() converts the port number from Host Byte Order (Little-Endian) + * to Network Byte Order (Big-Endian). + */ + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl(INADDR_ANY); + server_address.sin_port = htons(SERVER_PORT); + + /* * STEP 4: Bind the socket to the specified network interface and port. + * We cast the specific 'struct sockaddr_in' pointer to the generic + * 'struct sockaddr' pointer to satisfy the system function requirements. + */ + if (bind(server_fd, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { + perror("Bind operation failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + /* * STEP 5: Put the socket into listening mode. + * This tells the operating system kernel that the server is now active + * and ready to queue up incoming client connection requests. + */ + if (listen(server_fd, BACKLOG_SIZE) < 0) { + perror("Listen operation failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + printf("SUCCESS: Server is listening on port %d!\n", SERVER_PORT); + printf("Temporary: Sleeping for 20 seconds. Open another terminal and run 'ss -tulpn'\n"); + + /* Temporary sleep to keep the socket alive for your verification */ + sleep(20); + + /* Clean up the socket before exiting */ + close(server_fd); + printf("Server shut down successfully.\n"); + return 0; } \ No newline at end of file From 69444f6e34528110a636c43951168be03e582d83 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 13:56:59 +0200 Subject: [PATCH 4/9] Implemented server functionalities, handles requests and responds, but lacks safe exit --- lab7/Makefile | 3 +- lab7/src/server/main.c | 111 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/lab7/Makefile b/lab7/Makefile index 3a0e147..2a5d1d8 100644 --- a/lab7/Makefile +++ b/lab7/Makefile @@ -6,8 +6,9 @@ CC = gcc # -Werror: Treat all warnings as errors (strict code quality control) # -g: Generate debugging symbols for GDB and Valgrind # -std=c11: Use the ISO C11 standard +# -D_GNU_SOURCE: Allow Linux-specific solutions (like accept4() function) # -Iinclude: Look for header files inside the 'include' directory -CFLAGS = -Wall -Wextra -Werror -g -std=c11 -Iinclude +CFLAGS = -Wall -Wextra -Werror -g -std=c11 -D_GNU_SOURCE -Iinclude # ============================================================================== # Directory Structure diff --git a/lab7/src/server/main.c b/lab7/src/server/main.c index a73005e..131f8cc 100644 --- a/lab7/src/server/main.c +++ b/lab7/src/server/main.c @@ -5,7 +5,6 @@ #include #include -/* Define the port on which the server will listen */ #define SERVER_PORT 9000 /* * Defines the maximum number of pending connections in the queue. @@ -14,11 +13,16 @@ */ #define BACKLOG_SIZE 10 +#define BUFFER_SIZE 1024 + int main(void) { int server_fd; struct sockaddr_in server_address; int opt_reuse = 1; + /* Global request counter required by the assignment task */ + int request_counter = 0; + /* * STEP 1: Create the socket descriptor. * - AF_INET: Specifies the IPv4 protocol family. * - SOCK_STREAM: Specifies sequential, reliable TCP byte stream. @@ -76,15 +80,104 @@ int main(void) { exit(EXIT_FAILURE); } - printf("SUCCESS: Server is listening on port %d!\n", SERVER_PORT); - printf("Temporary: Sleeping for 20 seconds. Open another terminal and run 'ss -tulpn'\n"); - - /* Temporary sleep to keep the socket alive for your verification */ - sleep(20); + printf("SUCCESS: Server is fully operational and listening on port %d...\n", SERVER_PORT); - /* Clean up the socket before exiting */ - close(server_fd); - printf("Server shut down successfully.\n"); + /* ============================================================================== + * INFINITE SERVER LOOP (Iterative approach) + * ============================================================================== */ + while (1) { + int client_fd; + struct sockaddr_in client_address; + socklen_t client_addr_len = sizeof(client_address); + + char rx_buffer[BUFFER_SIZE]; + char tx_buffer[BUFFER_SIZE * 2]; + char http_body[BUFFER_SIZE]; + + ssize_t bytes_received; + + /* * STEP 1: Wait and accept an incoming connection. + * accept4() blocks the execution until a client connects. + * SOCK_CLOEXEC ensures the client socket descriptor is not leaked to child processes. + */ + client_fd = accept4(server_fd, (struct sockaddr *)&client_address, &client_addr_len, SOCK_CLOEXEC); + if (client_fd < 0) { + perror("Accept failed"); + continue; /* Do not crash the server, just wait for the next client */ + } + + /* * STEP 2: Read data sent by the client. + * We reserve the last byte of our buffer for the null-terminator '\0'. + */ + memset(rx_buffer, 0, sizeof(rx_buffer)); + bytes_received = recv(client_fd, rx_buffer, sizeof(rx_buffer) - 1, 0); + + if (bytes_received < 0) { + perror("Recv failed"); + close(client_fd); + continue; + } else if (bytes_received == 0) { + /* Client disconnected immediately without sending data */ + close(client_fd); + continue; + } + + /* * CRITICAL STEP FOR C: Null-terminate the received data. + * Network functions send raw bytes, not C-style strings. Without '\0', + * string functions like strncmp() or sscanf() would read out of bounds. + */ + rx_buffer[bytes_received] = '\0'; + + /* Print the first line of the received request to the server terminal */ + printf("--- NEW REQUEST RECEIVED ---\n%s\n----------------------------\n", rx_buffer); + + /* * STEP 3: Parse the request and execute business logic. + * We look for HTTP methods (GET/POST) or our custom command (ZADANIE). + */ + if (strncmp(rx_buffer, "GET", 3) == 0 || strncmp(rx_buffer, "POST", 4) == 0) { + + /* Increment counter for valid HTTP requests */ + request_counter++; + + /* 1. Generate the response text body first, so we can calculate its exact length */ + int body_len = snprintf(http_body, sizeof(http_body), "Liczba pobrań strony: %d", request_counter); + /* 2. Assemble the complete HTTP response including headers and the body */ + snprintf(tx_buffer, sizeof(tx_buffer), + "HTTP/1.1 200 OK\r\n" + "Server: Zajeciowy serwer SO\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "Connection: close\r\n" + "Cache-Control: no-store\r\n" + "Content-Length: %d\r\n\r\n" + "%s", + body_len, http_body); + + /* 3. Send the formatted HTTP package back to the client */ + send(client_fd, tx_buffer, strlen(tx_buffer), MSG_NOSIGNAL); + + } else if (strncmp(rx_buffer, "ZADANIE", 7) == 0) { + int task_value = 0; + + /* Extract the integer value following the "ZADANIE" keyword */ + if (sscanf(rx_buffer, "ZADANIE %d", &task_value) == 1) { + request_counter += task_value; + } + + /* Format response text WITHOUT any HTTP headers, as requested */ + snprintf(tx_buffer, sizeof(tx_buffer), "Liczba pobrań strony: %d", request_counter); + + /* Send raw text response */ + send(client_fd, tx_buffer, strlen(tx_buffer), MSG_NOSIGNAL); + } + + /* * STEP 4: Close the connection. + * This triggers the TCP FIN handshake, releasing the client socket resources. + */ + close(client_fd); + } + + /* Unreachable code in this architecture, but good practice for cleanup */ + close(server_fd); return 0; } \ No newline at end of file From c9dfd8aefa2513d5aea8ac1f3269a637fc99e284 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 14:23:58 +0200 Subject: [PATCH 5/9] Added signal handling for server termination --- lab7/src/server/main.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/lab7/src/server/main.c b/lab7/src/server/main.c index 131f8cc..0d3738e 100644 --- a/lab7/src/server/main.c +++ b/lab7/src/server/main.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #define SERVER_PORT 9000 @@ -15,6 +17,20 @@ #define BUFFER_SIZE 1024 +/* * Global flag used to control the main server loop execution. + * volatile: Tells the compiler not to optimize this variable into a register. + * sig_atomic_t: Guaranteed to be accessed atomically even during an interrupt. + */ +volatile sig_atomic_t keep_running = 1; + +/* * Signal handler function for SIGINT (Ctrl+C). + * This function must execute as fast as possible and only perform safe operations. + */ +void handle_sigint(int sig) { + (void)sig; /* Suppress unused parameter warning */ + keep_running = 0; /* Just flip the switch, let main handle the cleanup */ +} + int main(void) { int server_fd; struct sockaddr_in server_address; @@ -23,6 +39,21 @@ int main(void) { /* Global request counter required by the assignment task */ int request_counter = 0; + struct sigaction sa; + + /* ============================================================================== + * SIGNAL CONFIGURATION (Modern sigaction approach) + * ============================================================================== */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_sigint; /* Point to our custom handler function */ + sigemptyset(&sa.sa_mask); /* Do not block any other signals during execution */ + sa.sa_flags = 0; /* Do NOT use SA_RESTART. We WANT system calls to fail with EINTR */ + + if (sigaction(SIGINT, &sa, NULL) < 0) { + perror("Failed to register SIGINT handler"); + exit(EXIT_FAILURE); + } + /* * STEP 1: Create the socket descriptor. * - AF_INET: Specifies the IPv4 protocol family. * - SOCK_STREAM: Specifies sequential, reliable TCP byte stream. @@ -85,7 +116,7 @@ int main(void) { /* ============================================================================== * INFINITE SERVER LOOP (Iterative approach) * ============================================================================== */ - while (1) { + while (keep_running) { int client_fd; struct sockaddr_in client_address; socklen_t client_addr_len = sizeof(client_address); @@ -177,7 +208,12 @@ int main(void) { close(client_fd); } - /* Unreachable code in this architecture, but good practice for cleanup */ + /* ============================================================================== + * SAFE CLEANUP SECTION + * ============================================================================== */ + printf("Closing server listening socket and releasing resources...\n"); close(server_fd); - return 0; + + printf("Final application state: REQUEST_COUNTER = %d\n", request_counter); + printf("Server shut down gracefully. Goodbye!\n"); } \ No newline at end of file From f3c1f0263a5fac053486c945ed32a832405a4413 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 15:42:24 +0200 Subject: [PATCH 6/9] Implemented client logic in client.c --- lab7/src/client/main.c | 104 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/lab7/src/client/main.c b/lab7/src/client/main.c index 00583a8..a928efa 100644 --- a/lab7/src/client/main.c +++ b/lab7/src/client/main.c @@ -1,4 +1,104 @@ -// Minimal placeholder for the client application -int main(void) { +#include +#include +#include +#include +#include +#include +#include /* Required for modern inet_pton() */ + +#define BUFFER_SIZE 1024 + +int main(int argc, char *argv[]) { + int socket_fd; + struct sockaddr_in server_address; + + char tx_buffer[BUFFER_SIZE]; + char rx_buffer[BUFFER_SIZE]; + ssize_t bytes_received; + + /* * STEP 1: Validate command-line arguments. + * Expected format: ./client + * argc must be exactly 4 (program name + 3 arguments). + */ + if (argc != 4) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + /* * STEP 2: Create the client socket. + * We use SOCK_STREAM for TCP and SOCK_CLOEXEC as a modern safety measure. + */ + socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (socket_fd < 0) { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + /* * STEP 3: Configure the target server address structure. + * - sin_family: Always AF_INET for IPv4. + * - inet_pton: Modern, safe function to convert the text IP (e.g., "127.0.0.1") + * into the binary network byte format. Returns 1 on success. + * - htons + atoi: Convert the text port argument into a 16-bit integer + * and flip its endianness to Network Byte Order. + */ + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + + if (inet_pton(AF_INET, argv[1], &server_address.sin_addr) <= 0) { + fprintf(stderr, "Error: Invalid IPv4 address provided: %s\n", argv[1]); + close(socket_fd); + exit(EXIT_FAILURE); + } + + server_address.sin_port = htons(atoi(argv[2])); + + /* * STEP 4: Establish the TCP connection with the server. + * This call triggers the 3-way handshake under the hood. + * We cast 'struct sockaddr_in*' to the generic 'struct sockaddr*' type. + */ + if (connect(socket_fd, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { + perror("Connection to the server failed"); + close(socket_fd); + exit(EXIT_FAILURE); + } + + /* * STEP 5: Format and send the protocol message. + * We prepare the exact "ZADANIE LICZBA" format required by the task description. + */ + snprintf(tx_buffer, sizeof(tx_buffer), "ZADANIE %s", argv[3]); + + /* MSG_NOSIGNAL prevents the application from crashing if the server dropped early */ + if (send(socket_fd, tx_buffer, strlen(tx_buffer), MSG_NOSIGNAL) < 0) { + perror("Failed to send data to the server"); + close(socket_fd); + exit(EXIT_FAILURE); + } + + /* * STEP 6: Receive the text response from the server. + * We leave 1 byte at the end of the buffer for the null-terminator. + */ + memset(rx_buffer, 0, sizeof(rx_buffer)); + bytes_received = recv(socket_fd, rx_buffer, sizeof(rx_buffer) - 1, 0); + + if (bytes_received < 0) { + perror("Failed to receive data from the server"); + close(socket_fd); + exit(EXIT_FAILURE); + } else if (bytes_received == 0) { + fprintf(stderr, "Error: Server closed the connection unexpectedly.\n"); + close(socket_fd); + exit(EXIT_FAILURE); + } + + /* * STEP 7: Safe text processing and output. + * Null-terminate the raw network bytes to safely print them to stdout. + */ + rx_buffer[bytes_received] = '\0'; + printf("%s\n", rx_buffer); + + /* * STEP 8: Clean up. + * Close the descriptor and return success. + */ + close(socket_fd); return 0; } \ No newline at end of file From 1fe17a8e2eae864685c9971ee3b8dc4ae1f39519 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 15:56:34 +0200 Subject: [PATCH 7/9] Added an automated runner script w/ tmux --- lab7/scripts/test_automation.sh | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100755 lab7/scripts/test_automation.sh diff --git a/lab7/scripts/test_automation.sh b/lab7/scripts/test_automation.sh new file mode 100755 index 0000000..86f1917 --- /dev/null +++ b/lab7/scripts/test_automation.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# ============================================================================== +# BULLETPROOF TMUX Automation Script for Socket Server/Client Testing +# ============================================================================== + +SESSION_NAME="socket_test_session" + +# Calculate the absolute path of the project root +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# Change the working directory of this script execution to the project root +cd "$PROJECT_ROOT" || exit 1 + +# Ensure tmux is installed in WSL2 +if ! command -v tmux &> /dev/null; then + echo "Error: tmux is not installed. Please run 'sudo apt install tmux' first." + exit 1 +fi + +# Ensure the binaries exist before running the automation +if [ ! -f "./bin/server" ] || [ ! -f "./bin/client" ]; then + echo "Error: Binaries not found. Please build the project using 'make' first." + exit 1 +fi + +echo "Starting automated test architecture in: $PROJECT_ROOT" + +# 1. Start a new detached tmux session +tmux new-session -d -s "$SESSION_NAME" -n "Main" + +# 2. FORCE Pane 0 (Server) to change directory to project root via explicit send-keys +# This overrides any global shell settings or .bashrc profiles +tmux send-keys -t "$SESSION_NAME:Main.0" "cd \"$PROJECT_ROOT\"" C-m +tmux send-keys -t "$SESSION_NAME:Main.0" "./bin/server" C-m + +# 3. Give the server a moment to bind to port 9000 +sleep 1.5 + +# 4. Split the current window horizontally to create Pane 1 (Client) +tmux split-window -h -t "$SESSION_NAME:Main" + +# 5. Run the client automation in the BACKGROUND. +( + sleep 2.0 + + # FORCE Pane 1 (Client) to change directory to project root before sending commands + tmux send-keys -t "$SESSION_NAME:Main.1" "cd \"$PROJECT_ROOT\"" C-m + sleep 0.5 + + echo "-> Sending Request 1 (Value: 10) via client..." + tmux send-keys -t "$SESSION_NAME:Main.1" "./bin/client 127.0.0.1 9000 10" C-m + sleep 2.0 + + echo "-> Sending Request 2 (Value: 50) via client..." + tmux send-keys -t "$SESSION_NAME:Main.1" "./bin/client 127.0.0.1 9000 50" C-m + sleep 2.0 + + echo "-> Sending Request 3 (Value: 15) via client..." + tmux send-keys -t "$SESSION_NAME:Main.1" "./bin/client 127.0.0.1 9000 15" C-m + sleep 3.0 + + # Trigger graceful shutdown of the server (sending SIGINT via Ctrl+C) + echo "-> Triggering graceful server shutdown (sending SIGINT)..." + tmux send-keys -t "$SESSION_NAME:Main.0" C-c +) & + +# 6. Immediately attach to the tmux session so the user can watch the live show +echo "Attaching to tmux session... Watch the automation live!" +tmux attach-session -t "$SESSION_NAME" \ No newline at end of file From 347bc889023a9fb2b47f2d8cc1e0f2b8f8eb58a4 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 15:58:04 +0200 Subject: [PATCH 8/9] GH Actions CI pipeline update: added lab7 --- .github/workflows/make.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml index 2df8abf..7e5b877 100644 --- a/.github/workflows/make.yml +++ b/.github/workflows/make.yml @@ -82,6 +82,15 @@ jobs: cd .. fi + - name: Build Lab 7 (Sockets) + run: | + if [ -d "lab7" ]; then + cd lab7 + # We call specific targets because our Makefile's 'all' just prints a message + make all + cd .. + fi + - name: Success Notification run: echo "All laboratories compiled successfully!" \ No newline at end of file From 71b2f939c24ff534ff3e7534847db726adff3e47 Mon Sep 17 00:00:00 2001 From: Wiktor Trybus Date: Tue, 26 May 2026 16:06:39 +0200 Subject: [PATCH 9/9] Local and global README.md updates --- README.md | 1 + lab7/README.md | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 lab7/README.md diff --git a/README.md b/README.md index 1a96100..c8235d0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ The projects focus on low-level system programming in UNIX/Linux environments, u | [`lab4/`](./lab4) | POSIX Message Queues (IPC) | A "hub and spoke" communication system where multiple independent terminal clients exchange text messages in real-time using `/dev/mqueue`. Features asynchronous I/O managed by duplicating processes via `fork()`. | | [`lab5/`](./lab5) | POSIX Semaphores & Shared Memory | Implementation of the classic Producer-Consumer problem within a multi-process architecture. Leverages POSIX shared memory for zero-copy data transfer and POSIX semaphores for strict access synchronization, complete with a Manager daemon mitigating starvation via an aging algorithm. | | [`lab6/`](./lab6) | POSIX Threads, RTOS scheduling | A comprehensive, multithreaded simulation of a mobile robot's internal operating system. POSIX threads (pthreads), complex inter-thread communication, synchronization mechanisms, and Real-Time Operating System (RTOS) scheduling. | +| [`lab7/`](./lab7) | TCP/IP Sockets, Dual-Protocol HTTP Server | An iterative TCP server and client implementation featuring dual-protocol support (HTTP/1.1 and custom text commands). Focuses on network engineering patterns, including modern Linux `accept4()` with `SOCK_CLOEXEC`, graceful shutdown using `sigaction()` to intercept `EINTR`, and live integration testing via a `tmux` automation script. | ## ⚙️ System Requirements diff --git a/lab7/README.md b/lab7/README.md new file mode 100644 index 0000000..9bd9e98 --- /dev/null +++ b/lab7/README.md @@ -0,0 +1,133 @@ +# Iterative TCP Server and Client with HTTP Support + +A C-based network application implementing an iterative TCP server with dual-protocol parsing capability (standard HTTP/1.1 and a custom text-based protocol) alongside a dedicated client and a TMUX-driven test automation harness. Built specifically for Linux/WSL2 environments utilizing modern POSIX and jądra system systems mechanisms. + +## Project Overview + +The project consists of two primary applications communicating over TCP/IPv4: + +1. **The Server (`server`)**: + - Operates as an iterative network listener on port `9000` across all available interfaces (`INADDR_ANY`). + - Maintains an internal state variable: `REQUEST_COUNTER` (initialized at 0). + - Dynamically parses incoming stream data: + - **HTTP GET/POST Requests**: Increments the counter by 1 and replies with a valid HTTP/1.1 200 OK plain-text response containing the string `"Liczba pobrań strony: %d"`. + - **Custom "ZADANIE " Command**: Increments the counter by the specified integer value and responds immediately with the raw string payload (omitting HTTP headers). + - Implements robust system engineering patterns including graceful shutdown handling (`SIGINT`), socket resource preservation (`SO_REUSEADDR`), and leak prevention (`SOCK_CLOEXEC`). + +2. **The Client (`client`)**: + - A command-line network utility accepting three arguments: Server IPv4, Port, and a Number. + - Formats the command as `"ZADANIE "`, establishes a reliable TCP connection, transmits the payload, prints the raw server response, and exits cleanly. + +## Key Architectural Highlights + +- **Modern Network Extensions**: Utilizes Linux-specific `accept4()` with `SOCK_CLOEXEC` to prevent descriptor leakage across process forks. +- **Graceful Shutdown Hook**: Replaces standard unsafe signal routines with a POSIX-compliant `sigaction()` structure. Interrupted blocking calls (returning `EINTR`) are gracefully intercepted to allow systematic garbage collection of network descriptors. +- **Signal Resilience**: Uses `MSG_NOSIGNAL` on data transmissions (`send()`) to mask `SIGPIPE` generation, securing the process against unexpected client socket dropouts. +- **Byte Order Integrity**: Enforces structural conversion between Host Byte Order (Little-Endian) and Network Byte Order (Big-Endian) using `htons()` and `htonl()` wrappers. + +## Project Structure + +```text +. +├── Makefile # Strict compilation definition file +├── bin/ # Compiled production binaries (server, client) +├── obj/ # Temporary translation object files (.o) +├── include/ # Universal project header declarations +├── scripts/ # Production-ready shell scripts +│ └── test_automation.sh # Complete TMUX automation deployment harness +└── src/ # Implementation source files + ├── client/ + │ └── main.c # Client source entry point + └── server/ + └── main.c # Server source entry point +``` + +## Prerequisites + +* **Operating System**: Linux kernel 2.6.28+ or WSL2 (Windows Subsystem for Linux). +* **Toolchain**: `gcc` compiler supporting the ISO C11 standard and `GNU make`. +* **System Diagnostics**: `tmux` (required for automated sequence monitoring), `iproute2` (`ss`), and `netcat` (`nc`). + +To install dependencies on Debian/Ubuntu-based environments: + +```bash +sudo apt update && sudo apt install build-essential tmux iproute2 netcat-openbsd -y +``` + +## Compilation + +The project uses `GNU Make` with strict error policing (`-Wall -Wextra -Werror`). Any compiler warnings will abort the compilation pipeline to enforce code reliability. + +To compile both target binaries: + +```bash +make +``` + +To purge object layers and binaries: + +```bash +make clean +``` + +## Execution and Usage + +### Manual Operations + +1. **Initialize the Listener Node**: +```bash +./bin/server +``` + + +2. **Execute a Custom Client Call**: +```bash +./bin/client 127.0.0.1 9000 42 +``` + + + +### Interface Diagnostics + +* **Web Browser Validation**: Point any standard web browser to `http://127.0.0.1:9000`. The browser will submit a speculative `GET` sequence, advancing the server state machine and outputting the counter. +* **Raw Diagnostic Injection via Netcat**: +```bash +echo -n "ZADANIE 10" | nc 127.0.0.1 9000 +``` + + +* **Socket Allocation Tracking**: Check socket listening states via `ss`: +```bash +ss -tulpn | grep 9000 +``` + + + +## Automated Evaluation Environment + +The repository includes an automated integration test suite located within `scripts/test_automation.sh`. + +This shell script dynamically: + +1. Validates the filesystem topology and existence of compiled targets. +2. Initializes a detached background `tmux` instance. +3. Automatically maps directory contexts to mitigate jądra system defaults. +4. Spawns the server block on Pane 0, inserts a calculated polling threshold, splits the matrix horizontally, and queues up programmatic `client` executions on Pane 1. +5. Injects automated text sequences, handles real-time visual output, signals a system `SIGINT` interruption vector to trigger the server's internal resource destructor, and attaches to the live buffer. + +To execute the automation framework: + +```bash +chmod +x scripts/test_automation.sh +./scripts/test_automation.sh +``` + +## Memory Allocation Profile + +The application has been audited under binary translation tools (`Valgrind Memcheck`) to confirm absence of heap fragmentation, descriptor pollution, or memory leaks. + +```bash +valgrind --leak-check=full ./bin/server +``` + +Expected execution signature: `All heap blocks were freed -- no leaks are possible`. \ No newline at end of file