int create_listen_socket(int port)
{
int sockfd;
struct sockaddr_in addr;
int opt = 1;
/* Create TCP socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
log_message(LOG_ERROR, "Socket creation failed: %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Set SO_REUSEADDR option */
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/* Configure bind address */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
/* Bind socket to port */
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
log_message(LOG_ERROR, "Bind failed on port %d: %s", port, strerror(errno));
close(sockfd);
exit(EXIT_FAILURE);
}
/* Start listening */
if (listen(sockfd, SOMAXCONN) < 0)
{
log_message(LOG_ERROR, "Listen failed: %s", strerror(errno));
close(sockfd);
exit(EXIT_FAILURE);
}
log_message(LOG_INFO, "Listening on port %d", port);
return sockfd;
}
/*
* Copyright (c) 2025. All rights reserved.
*
* File: server.c
* Description: HTTP server implementation with thread pool management
* Author: Chen Xinxin
* Date: 2025-08-01
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/sendfile.h>
#include "utils.h"
#include "http_parser.h"
#define MAX_THREADS 100 /* Maximum concurrent threads */
#define BUFFER_SIZE 8192 /* Client request buffer size */
sem_t thread_semaphore; /* Semaphore for thread pool management */
/**
* @struct thread_args
* @brief Client connection context for thread handling
*/
typedef struct
{
int client_fd; /* Client socket descriptor */
struct sockaddr_in client_addr; /* Client address information */
} thread_args;
/**
* @fn static void *handle_client(void *arg)
* @brief Handles client connection in a separate thread
*
* @param arg Thread arguments containing client context
* @return NULL (thread exits after processing)
*/
static void *handle_client(void *arg)
{
thread_args *args = (thread_args *)arg;
char buffer[BUFFER_SIZE];
/* Extract client IP and port information */
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(args->client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
int client_port = ntohs(args->client_addr.sin_port);
log_message(LOG_DEBUG, "New connection from %s:%d", client_ip, client_port);
/* Read HTTP request from client */
ssize_t bytes_read = read(args->client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read <= 0)
{
if (bytes_read == 0)
{
log_message(LOG_DEBUG, "Client %s:%d disconnected", client_ip, client_port);
}
else
{
log_message(LOG_WARN, "Read error from %s:%d: %s",
client_ip, client_port, strerror(errno));
}
close(args->client_fd);
free(args);
sem_post(&thread_semaphore);
return NULL;
}
buffer[bytes_read] = '\0';
/* Check for request buffer overflow */
if (bytes_read >= BUFFER_SIZE - 1)
{
log_message(LOG_WARN, "Request too large from %s:%d", client_ip, client_port);
dprintf(args->client_fd, "HTTP/1.1 413 Payload Too Large\r\n\r\n");
close(args->client_fd);
free(args);
sem_post(&thread_semaphore);
return NULL;
}
/* Parse HTTP request */
http_request req;
if (parse_http_request(buffer, &req) < 0)
{
log_message(LOG_WARN, "Bad request from %s:%d", client_ip, client_port);
dprintf(args->client_fd, "HTTP/1.1 400 Bad Request\r\n\r\n");
close(args->client_fd);
free(args);
sem_post(&thread_semaphore);
return NULL;
}
log_message(LOG_INFO, "%s %s from %s:%d", req.method, req.path, client_ip, client_port);
/* Process request based on HTTP method */
if (strcasecmp(req.method, "GET") == 0)
{
log_message(LOG_DEBUG, "Processing GET request for %s", req.path);
process_request(&req, args->client_fd);
}
else if (strcasecmp(req.method, "POST") == 0)
{
if (strcmp(req.path, "/data/contact.json") == 0)
{
log_message(LOG_INFO, "Processing contact form from %s:%d", client_ip, client_port);
handle_contact_form(&req, args->client_fd);
}
else
{
log_message(LOG_WARN, "Unsupported POST path: %s from %s:%d",
req.path, client_ip, client_port);
dprintf(args->client_fd, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
}
}
else
{
log_message(LOG_WARN, "Unsupported method: %s from %s:%d",
req.method, client_ip, client_port);
dprintf(args->client_fd, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
}
/* Cleanup after request processing */
close(args->client_fd);
free(args);
sem_post(&thread_semaphore);
return NULL;
}
/**
* @fn int main(int argc, char *argv[])
* @brief Main server entry point
*
* @param argc Command line argument count
* @param argv Command line arguments
* @return Exit status (0 = success)
*/
int main(int argc, char *argv[])
{
int port = 80; /* Default HTTP port */
/* Initialize logging system */
init_logger(argv[0]);
log_message(LOG_INFO, "Starting server on port %d", port);
/* Process command-line arguments */
if (argc >= 2)
{
char *endptr;
long input_port = strtol(argv[1], &endptr, 10);
if (*endptr != '\0' || input_port < 1 || input_port > 65535)
{
fprintf(stderr, "Error: Invalid port number '%s', using default port 80\n", argv[1]);
}
else
{
port = (int)input_port;
}
}
/* Initialize semaphore for thread pool */
if (sem_init(&thread_semaphore, 0, MAX_THREADS) != 0)
{
log_message(LOG_ERROR, "Semaphore initialization failed: %s", strerror(errno));
return EXIT_FAILURE;
}
/* Create listening socket */
int server_fd = create_listen_socket(port);
if (server_fd < 0)
{
log_message(LOG_ERROR, "Failed to create listen socket");
return EXIT_FAILURE;
}
pthread_t thread_id;
log_message(LOG_INFO, "Server started, waiting for connections...");
/* Main server loop */
while (1)
{
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
if (client_fd < 0)
{
log_message(LOG_ERROR, "Accept failed: %s", strerror(errno));
continue;
}
/* Wait for available thread slot */
sem_wait(&thread_semaphore);
/* Allocate thread arguments */
thread_args *args = malloc(sizeof(thread_args));
if (!args)
{
log_message(LOG_ERROR, "Memory allocation failed for client");
close(client_fd);
sem_post(&thread_semaphore);
continue;
}
args->client_fd = client_fd;
args->client_addr = client_addr;
/* Create detached thread */
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&thread_id, &attr, handle_client, args) != 0)
{
log_message(LOG_ERROR, "Thread creation failed: %s", strerror(errno));
free(args);
close(client_fd);
sem_post(&thread_semaphore);
}
pthread_attr_destroy(&attr);
}
/* Cleanup resources */
close(server_fd);
sem_destroy(&thread_semaphore);
return 0;
}
/*
* Copyright (c) 2025. All rights reserved.
*
* File: server.c
* Description: HTTP server implementation with multi-process architecture
* Author: Chen Xinxin
* Date: 2025-08-01
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "utils.h"
#include "http_parser.h"
#define MAX_PROCESSES 10 /* Maximum number of child processes */
pid_t children[MAX_PROCESSES]; /* Array to store child process PIDs */
int server_fd; /* Server socket descriptor for signal handling */
/**
* @fn void handle_sigint(int sig)
* @brief Signal handler for graceful server shutdown
*
* @param sig Signal number (SIGINT or SIGTERM)
*/
void handle_sigint(int sig)
{
(void)sig;
log_message(LOG_INFO, "Received termination signal, cleaning resources...");
/* Terminate all child processes */
for (int i = 0; i < MAX_PROCESSES; i++)
{
if (children[i] > 0)
{
log_message(LOG_INFO, "Terminating child process PID: %d", children[i]);
kill(children[i], SIGTERM); /* Send termination signal */
waitpid(children[i], NULL, 0); /* Wait for process termination */
}
}
close(server_fd); /* Close server socket */
log_message(LOG_INFO, "Server has been safely shut down");
close_logger(); /* Close logging system */
exit(EXIT_SUCCESS);
}
/**
* @fn int main(int argc, char *argv[])
* @brief Main entry point for HTTP server with process-based concurrency
*
* @param argc Command-line argument count
* @param argv Command-line arguments
* @return Exit status (0 = success)
*/
int main(int argc, char *argv[])
{
int port = 80; /* Default HTTP port */
/* Initialize logging system */
init_logger(argv[0]);
log_message(LOG_INFO, "Starting server on port %d", port);
/* Register signal handlers for graceful shutdown */
signal(SIGINT, handle_sigint); /* Ctrl+C */
signal(SIGTERM, handle_sigint); /* kill command */
/* Process command-line arguments */
if (argc >= 2)
{
char *endptr;
long input_port = strtol(argv[1], &endptr, 10); /* Safe conversion */
/* Validate port format and range */
if (*endptr != '\0' || input_port < 1 || input_port > 65535)
{
log_message(LOG_WARN, "Invalid port number '%s', using default port 80", argv[1]);
}
else
{
port = (int)input_port;
}
}
log_message(LOG_INFO, "Using port: %d", port);
/* Create listening socket */
server_fd = create_listen_socket(port);
if (server_fd < 0)
{
log_message(LOG_ERROR, "Failed to create listening socket");
return EXIT_FAILURE;
}
log_message(LOG_INFO, "Listening socket created successfully, fd=%d", server_fd);
/* Ignore SIGCHLD to prevent zombie processes */
signal(SIGCHLD, SIG_IGN);
/* Fork worker processes */
for (int i = 0; i < MAX_PROCESSES; i++)
{
pid_t pid = fork();
if (pid < 0)
{
log_message(LOG_ERROR, "fork failed: %s", strerror(errno));
close(server_fd);
return EXIT_FAILURE;
}
/* Child process execution */
if (pid == 0)
{
log_message(LOG_INFO, "Child process %d started", getpid());
/* Child process request handling loop */
while (1)
{
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
if (client_fd < 0)
{
if (errno == EINTR)
continue; /* Interrupted by signal, retry */
log_message(LOG_ERROR, "accept failed: %s", strerror(errno));
continue;
}
/* Extract client information */
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
log_message(LOG_INFO, "Accepted connection from %s:%d",
client_ip, ntohs(client_addr.sin_port));
/* Read HTTP request */
char buffer[8192];
ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer) - 1);
if (bytes_read <= 0)
{
if (bytes_read == 0)
{
log_message(LOG_INFO, "Client disconnected");
}
else
{
log_message(LOG_ERROR, "Read failed: %s", strerror(errno));
}
close(client_fd);
continue;
}
buffer[bytes_read] = '\0';
log_message(LOG_DEBUG, "Received request:\n%.*s", (int)bytes_read, buffer);
/* Parse HTTP request */
http_request req;
if (parse_http_request(buffer, &req) < 0)
{
log_message(LOG_WARN, "Failed to parse HTTP request");
dprintf(client_fd, "HTTP/1.1 400 Bad Request\r\n\r\n");
close(client_fd);
continue;
}
log_message(LOG_INFO, "Processing %s request: %s", req.method, req.path);
/* Route request based on method */
if (strcasecmp(req.method, "GET") == 0)
{
process_request(&req, client_fd);
}
else if (strcasecmp(req.method, "POST") == 0)
{
if (strcmp(req.path, "/data/contact.json") == 0)
{
handle_contact_form(&req, client_fd);
}
else
{
log_message(LOG_WARN, "Unsupported POST path: %s", req.path);
dprintf(client_fd, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
}
}
else
{
log_message(LOG_WARN, "Unsupported HTTP method: %s", req.method);
dprintf(client_fd, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
}
close(client_fd);
log_message(LOG_DEBUG, "Request processing completed");
}
exit(0); /* Child process exit */
}
else
{
children[i] = pid; /* Store child PID */
log_message(LOG_INFO, "Created child process PID: %d", pid);
}
}
/* Parent process cleanup */
close(server_fd);
log_message(LOG_INFO, "Parent process closed listening socket");
/* Parent process idle loop */
log_message(LOG_INFO, "Server is running, press Ctrl+C to stop");
printf("Server is running, press Ctrl+C to stop\n");
while (1)
{
pause(); /* Wait for termination signal */
}
return 0;
}
/*
* Copyright (c) 2025. All rights reserved.
*
* File: server.c
* Description: HTTP server implementation using select() for I/O multiplexing
* Author: Chen Xinxin
* Date: 2025-08-01
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <libgen.h>
#include <sys/stat.h>
#include <ctype.h>
#include <netinet/in.h>
#include "utils.h"
#include "http_parser.h"
#define MAX_CLIENTS 1024 /* Maximum simultaneous client connections */
/**
* @fn void accept_new_connection(int server_fd, fd_set *read_fds, int *max_fd)
* @brief Accepts new client connection and updates select() descriptor set
*
* @param server_fd Server socket descriptor
* @param read_fds Pointer to read file descriptor set
* @param max_fd Pointer to current maximum file descriptor
*/
void accept_new_connection(int server_fd, fd_set *read_fds, int *max_fd)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0)
{
log_message(LOG_ERROR, "accept failed: %s", strerror(errno));
return;
}
/* Extract client IP and port information */
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
int client_port = ntohs(client_addr.sin_port);
/* Validate client FD against connection limit */
if (client_fd >= MAX_CLIENTS)
{
log_message(LOG_WARN, "Client %s:%d exceeds connection limit (fd=%d)",
client_ip, client_port, client_fd);
dprintf(client_fd, "HTTP/1.1 503 Service Unavailable\r\n\r\n");
close(client_fd);
return;
}
/* Add new client to select set */
FD_SET(client_fd, read_fds);
if (client_fd > *max_fd)
*max_fd = client_fd;
log_message(LOG_INFO, "Accepted new connection from %s:%d (fd=%d)",
client_ip, client_port, client_fd);
}
/**
* @fn void handle_client(int client_fd, fd_set *read_fds)
* @brief Processes client request and sends appropriate HTTP response
*
* @param client_fd Client socket descriptor
* @param read_fds Pointer to read file descriptor set
*/
void handle_client(int client_fd, fd_set *read_fds)
{
char buffer[BUFFER_SIZE] = {0};
ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer) - 1);
/* Handle read errors and disconnections */
if (bytes_read <= 0)
{
if (bytes_read == 0)
{
log_message(LOG_INFO, "Client fd=%d disconnected", client_fd);
}
else
{
log_message(LOG_ERROR, "Read error from client fd=%d: %s",
client_fd, strerror(errno));
}
FD_CLR(client_fd, read_fds);
close(client_fd);
return;
}
buffer[bytes_read] = '\0';
log_message(LOG_DEBUG, "Received request from fd=%d:\n%.*s",
client_fd, (int)bytes_read, buffer);
/* Parse HTTP request */
http_request req;
if (parse_http_request(buffer, &req) < 0)
{
log_message(LOG_WARN, "Failed to parse HTTP request from fd=%d", client_fd);
dprintf(client_fd, "HTTP/1.1 400 Bad Request\r\n\r\n");
FD_CLR(client_fd, read_fds);
close(client_fd);
return;
}
log_message(LOG_INFO, "Processing %s request from fd=%d: %s",
req.method, client_fd, req.path);
/* Route request based on HTTP method */
if (strcasecmp(req.method, "GET") == 0)
{
log_message(LOG_DEBUG, "Processing GET request for fd=%d", client_fd);
process_request(&req, client_fd);
}
else if (strcasecmp(req.method, "POST") == 0)
{
if (strcmp(req.path, "/data/contact.json") == 0)
{
log_message(LOG_DEBUG, "Processing contact form submission for fd=%d", client_fd);
handle_contact_form(&req, client_fd);
}
else
{
log_message(LOG_WARN, "Unsupported POST path for fd=%d: %s",
client_fd, req.path);
dprintf(client_fd, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
}
}
else
{
log_message(LOG_WARN, "Unsupported HTTP method for fd=%d: %s",
client_fd, req.method);
dprintf(client_fd, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
}
/* Cleanup after request processing */
FD_CLR(client_fd, read_fds);
close(client_fd);
log_message(LOG_DEBUG, "Completed processing request for fd=%d", client_fd);
}
/**
* @fn int main(int argc, char *argv[])
* @brief Main entry point for select-based HTTP server
*
* @param argc Command-line argument count
* @param argv Command-line arguments
* @return Exit status (0 = success)
*/
int main(int argc, char *argv[])
{
int port = 80; /* Default HTTP port */
/* Initialize logging system */
init_logger(argv[0]);
log_message(LOG_INFO, "Starting server on port %d", port);
/* Process command-line arguments */
if (argc >= 2)
{
char *endptr;
long input_port = strtol(argv[1], &endptr, 10);
/* Validate port number */
if (*endptr != '\0' || input_port < 1 || input_port > 65535)
{
log_message(LOG_WARN, "Invalid port number '%s', using default port 80", argv[1]);
}
else
{
port = (int)input_port;
}
}
log_message(LOG_INFO, "Using port: %d", port);
/* Create listening socket */
int server_fd = create_listen_socket(port);
if (server_fd < 0)
{
log_message(LOG_ERROR, "Failed to create listening socket");
return EXIT_FAILURE;
}
log_message(LOG_INFO, "Listening socket created successfully, fd=%d", server_fd);
fd_set read_fds; /* File descriptor set for select() */
int max_fd = server_fd; /* Highest file descriptor number */
struct timeval timeout; /* select() timeout value */
log_message(LOG_INFO, "Web server started on port %d", port);
/* Main server loop */
while (1)
{
FD_ZERO(&read_fds);
FD_SET(server_fd, &read_fds);
/* Set select timeout to 5 seconds */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
/* Wait for socket activity */
int activity = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
if (activity < 0)
{
if (errno == EINTR)
{
log_message(LOG_DEBUG, "select interrupted by signal");
continue;
}
log_message(LOG_ERROR, "select failed: %s", strerror(errno));
break;
}
else if (activity == 0)
{
/* Timeout occurred with no activity */
log_message(LOG_DEBUG, "select timeout, no active connections");
continue;
}
/* Check for new connections on server socket */
if (FD_ISSET(server_fd, &read_fds))
{
accept_new_connection(server_fd, &read_fds, &max_fd);
}
/* Check existing client connections for data */
for (int i = 0; i <= max_fd; i++)
{
if (i != server_fd && FD_ISSET(i, &read_fds))
{
handle_client(i, &read_fds);
}
}
}
/* Cleanup resources */
close(server_fd);
log_message(LOG_INFO, "Server shutdown");
close_logger();
return 0;
}
根据这些函数写一下本系统的通信层系统设计包含两个大点,架构设计和核心机制,作为报告形式放在系统架构的通信层小节
最新发布