你是一个编程小白,拿到了以下的代码,如何从0基础逐渐理解以下代码的功能和实现方法:/* Copyright(c) 2025-2025 Chengdu TP-Link Technologies Co.Ltd.
*
* file httpdServer.c
* brief This is a simple web server, supports multi-program, multi-thread and multi-IO, and supports operation Get and Post.
*
* author Zhang Jiachong
* version 1.0.0
* date 01-08-25
*
* history \arg 1.0.0, 01-08-25, Zhang Jiachong, Create the file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <wait.h>
#include <sys/epoll.h>
/**************************************************************************************************/
/* DEFINES */
#define PORT 80
#define WEB_ROOT "./web"
#define THREAD_POOL_SIZE 4
#define MAX_EVENTS 100
/**************************************************************************************************/
/**************************************************************************************************/
/* TYPES */
enum method_enum
{
MULTI_PROG, /* Multi-process mode */
MULTI_THRD, /* Multi-thread mode */
MULTI_IO, /* I/O multiplexing mode */
THRD_POLL /* Thread pool mode */
};
typedef struct
{
void *(*task_function)(void *); /* Function pointer for request handling */
int client_fd;
} Task;
typedef struct
{
pthread_t *threads; /*Worker threads array*/
Task *task_queue; /*Task queue buffer*/
int queue_size;
int queue_capacity;
int head;
int tail;
int count;
pthread_mutex_t lock;
pthread_cond_t task_cond; /*Condition for task availability*/
pthread_cond_t space_cond; /*Condition for queue space*/
} ThreadPool;
/**************************************************************************************************/
/**************************************************************************************************/
/* EXTERN_PROTOTYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* LOCAL_PROTOTYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* VARIABLES */
int method = THRD_POLL;
/**************************************************************************************************/
/**************************************************************************************************/
/* LOCAL_FUNCTIONS */
static void set_nonblocking(int fd);
static void thread_pool_init(ThreadPool *pool, int size, int capacity);
static void *worker_thread(void *arg);
static void add_task(ThreadPool *pool, void *(*task_function)(void *), int client_fd);
static char *url_decode(const char *str);
static void log_request(const char *method, const char *path, int status, const char *status_msg);
static int send_response(int sock, int status, const char *status_msg, const char *content_type, const char *body, int body_len);
static void *handle_request(void *arg);
/**************************************************************************************************/
/**************************************************************************************************/
/* PUBLIC_FUNCTIONS */
/**************************************************************************************************/
static void set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
/*
* fn static void thread_pool_init(ThreadPool *pool, int size, int capacity)
* brief The thread pool initialization function.
*
* param[in] pool The thread pool.
* param[in] size The number of threads in the thread pool.
* param[in] capacity The maximum number of tasks in the thread pool.
*/
static void thread_pool_init(ThreadPool *pool, int size, int capacity)
{
pool->threads = malloc(size * sizeof(pthread_t));
pool->task_queue = malloc(capacity * sizeof(Task));
pool->queue_size = 0;
pool->queue_capacity = capacity;
pool->head = 0;
pool->tail = 0;
pool->count = 0;
pthread_mutex_init(&pool->lock, NULL);
pthread_cond_init(&pool->task_cond, NULL);
pthread_cond_init(&pool->space_cond, NULL);
for (int i = 0; i < size; i++)
{
pthread_create(&pool->threads[i], NULL, (void *)worker_thread, pool);
}
}
/*
* fn static void *worker_thread(void *arg)
* brief The working function of a thread pool thread.
*
* param[in] arg The thread pool.
*/
static void *worker_thread(void *arg)
{
ThreadPool *pool = (ThreadPool *)arg;
while (1)
{
pthread_mutex_lock(&pool->lock);
/* Wait for tasks if queue empty */
while (pool->queue_size == 0)
{
pthread_cond_wait(&pool->task_cond, &pool->lock);
}
Task task = pool->task_queue[pool->head];
pool->head = (pool->head + 1) % pool->queue_capacity;
pool->queue_size--;
pthread_cond_signal(&pool->space_cond);
pthread_mutex_unlock(&pool->lock);
int *sock_ptr = malloc(sizeof(int));
*sock_ptr = task.client_fd;
task.task_function(sock_ptr);
}
return NULL;
}
/*
* fn static void add_task(ThreadPool *pool, void *(*task_function)(void *), int client_fd)
* brief Add a new task to the task queue in the thread pool.
*
* param[in] pool The thread pool.
* param[in] task_function The function bound to the new task.
* param[in] client_fd The socket for browser clients.
*/
static void add_task(ThreadPool *pool, void *(*task_function)(void *), int client_fd)
{
pthread_mutex_lock(&pool->lock);
/* Wait for tasks if queue empty */
while (pool->queue_size == pool->queue_capacity)
{
pthread_cond_wait(&pool->space_cond, &pool->lock);
}
pool->task_queue[pool->tail].task_function = task_function;
pool->task_queue[pool->tail].client_fd = client_fd;
pool->tail = (pool->tail + 1) % pool->queue_capacity;
pool->queue_size++;
pthread_cond_signal(&pool->task_cond);
pthread_mutex_unlock(&pool->lock);
}
/*
* fn static char *url_decode(const char *str)
* brief Parses the URL in the Post.
*
* param[in] input_int The URL in the Post.
*
* return the parsesd result
*/
static char *url_decode(const char *str_in)
{
char *output = malloc(strlen(str_in) + 1);
if (!output)
return NULL;
/* (% encoding and + replacement handling) */
char *dst = output;
while (*str_in)
{
if (*str_in == '+')
{
*dst++ = ' ';
str_in++;
}
else if (*str_in == '%' && isxdigit(str_in[1]) && isxdigit(str_in[2]))
{
char hex[3] = {str_in[1], str_in[2], '\0'};
*dst++ = (char)strtol(hex, NULL, 16);
str_in += 3;
}
else
{
*dst++ = *str_in++;
}
}
*dst = '\0';
return output;
}
static void log_request(const char *method, const char *path, int status, const char *status_msg)
{
printf("[%s] %s -> %d %s\n", method, path, status, status_msg);
}
/*
* fn static void send_response(int sock, int status, const char *status_msg, const char *content_type, const char *body, int body_len)
* brief Send a response to the browser.
*
* param[in] sock Client socket.
* param[in] status Response status.
* param[in] status_msg Meaning of response status.
* param[in] content_type Type of body.
* param[in] body Data to be send.
* param[in] body_len Length of body.
*
* return the send result
*/
static int send_response(int sock, int status, const char *status_msg, const char *content_type, const char *body, int body_len)
{
char header[1024];
/* Build HTTP response header */
snprintf(header, sizeof(header),
"HTTP/1.1 %d %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n\r\n",
status, status_msg, content_type, body_len);
size_t result = 0;
result = send(sock, header, strlen(header), 0);
if (result == -1)
{
close(sock);
return -1;
}
if (body && body_len > 0)
{
send(sock, body, body_len, 0);
if (result == -1)
{
close(sock);
return -1;
}
}
return 0;
}
/*
* fn static void *handle_request(void *arg)
* brief Handle the request from browser.
*
* param[in] arg Pointer of the client socket.
*
* return meaningless
*/
static void *handle_request(void *arg)
{
int sock = *(int *)arg;
free(arg);
char buffer[4096];
size_t bytes_read = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (bytes_read <= 0)
{
close(sock);
return NULL;
}
buffer[bytes_read] = '\0';
char method[16], path[256];
sscanf(buffer, "%s %s", method, path);
if (strcmp(method, "GET") == 0)
{
char filepath[512];
if (strcmp(path, "/") == 0)
{
strcpy(path, "/Index.html");
}
sprintf(filepath, "%s%s", WEB_ROOT, path);
FILE *file = fopen(filepath, "rb");
if (file)
{
fseek(file, 0, SEEK_END);
long len = ftell(file);
fseek(file, 0, SEEK_SET);
char *content = malloc(len);
fread(content, 1, len, file);
fclose(file);
const char *content_type = "text/html";
if (strstr(path, ".css"))
{
content_type = "text/css";
}
else if (strstr(path, ".js"))
{
content_type = "application/json";
}
else if (strstr(path, ".png"))
{
content_type = "image/png";
}
else if (strstr(path, ".jpg"))
{
content_type = "image/jpeg";
}
send_response(sock, 200, "OK", content_type, content, len);
free(content);
log_request(method, path, 200, "OK");
}
else
{
const char *not_found = "<h1>404 Not Found</h1>";
send_response(sock, 404, "Not Found", "text/html", not_found, strlen(not_found));
log_request(method, path, 404, "Not Found");
}
}
else if (strcmp(method, "POST") == 0)
{
char *content_length_ptr = strstr(buffer, "Content-Length: ");
int content_length = 0;
if (content_length_ptr)
{
sscanf(content_length_ptr + 16, "%d", &content_length);
}
if (content_length <= 0)
{
const char *bad_request = "<h1>400 Bad Request: Missing Content-Length</h1>";
send_response(sock, 400, "Bad Request", "text/html", bad_request, strlen(bad_request));
log_request(method, path, 400, "Bad Request");
close(sock);
return NULL;
}
char *post_data = malloc(content_length + 1);
if (!post_data)
{
const char *server_error = "<h1>500 Internal Server Error</h1>";
send_response(sock, 500, "Server Error", "text/html", server_error, strlen(server_error));
log_request(method, path, 500, "Server Error");
close(sock);
return NULL;
}
char *body_start = strstr(buffer, "\r\n\r\n");
if (body_start)
{
body_start += 4;
memcpy(post_data, body_start, content_length);
}
else
{
const char *bad_request = "<h1>400 Bad Request: Bad Message Format</h1>";
send_response(sock, 400, "Bad Request", "text/html", bad_request, strlen(bad_request));
log_request(method, path, 400, "Bad Request");
close(sock);
return NULL;
}
post_data[content_length] = '\0';
/* post_data : name=z111&email=125253455%40qq.com&message=dasdsasd */
if (strcmp(path, "/data/contact.json") == 0)
{
char name[128] = {0};
char email[128] = {0};
char message[512] = {0};
char *token = strtok(post_data, "&");
while (token != NULL)
{
char *eq = strchr(token, '=');
if (eq)
{
*eq = '\0';
char *key = token;
char *value = url_decode(eq + 1);
if (strcmp(key, "name") == 0)
{
strncpy(name, value, sizeof(name) - 1);
}
else if (strcmp(key, "email") == 0)
{
strncpy(email, value, sizeof(email) - 1);
}
else if (strcmp(key, "message") == 0)
{
strncpy(message, value, sizeof(message) - 1);
}
free(value);
}
token = strtok(NULL, "&");
}
char response[1024];
snprintf(response, sizeof(response),
"{"
"\"callback\": \"Message received successfully\","
"\"details\": {"
"\"name\": %s,"
"\"email\": %s,"
"\"message\": %s"
"}"
"}",
name, email, message);
send_response(sock, 200, "OK", "application/json", response, strlen(response));
log_request(method, path, 200, "OK");
}
else
{
const char *not_implemented = "<h1>501 Not Implemented</h1>";
send_response(sock, 501, "Not Implemented", "text/html", not_implemented, strlen(not_implemented));
log_request(method, path, 501, "Not Implemented");
close(sock);
return NULL;
}
free(post_data);
}
else
{
const char *not_implemented = "<h1>501 Not Implemented</h1>";
send_response(sock, 501, "Not Implemented", "text/html", not_implemented, strlen(not_implemented));
log_request(method, path, 501, "Not Implemented");
close(sock);
return NULL;
}
close(sock);
return NULL;
}
int main()
{
/* 当client先终止连接,server向socket中写文件时会触发SIGPIPE信号导致程序退出 */
/* 此处忽略SIGPIPE信号,在send_response中做处理 */
signal(SIGPIPE, SIG_IGN);
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == server_fd)
{
return -1;
}
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = htonl(INADDR_ANY)};
bind(server_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(server_fd, 10);
printf("Web server running on port %d\n", PORT);
if (MULTI_IO == method || THRD_POLL == method)
{
int epoll_fd = epoll_create1(0);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN;
ev.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);
ThreadPool thread_pool;
if (THRD_POLL == method)
{
thread_pool_init(&thread_pool, THREAD_POOL_SIZE, 256);
}
while (1)
{
int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++)
{
if (events[i].data.fd == server_fd)
{
int client_fd = accept(server_fd, NULL, NULL);
if (THRD_POLL == method)
{
set_nonblocking(client_fd);
}
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);
}
else
{
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
if (THRD_POLL == method)
{
add_task(&thread_pool, handle_request, events[i].data.fd);
}
else
{
int *sock_ptr = malloc(sizeof(int));
*sock_ptr = events[i].data.fd;
handle_request(sock_ptr);
}
}
}
}
}
else
{
while (1)
{
int client_fd = accept(server_fd, NULL, NULL);
int *sock_ptr = malloc(sizeof(int));
*sock_ptr = client_fd;
if (MULTI_PROG == method)
{
pid_t pid = fork();
if (pid == 0)
{
close(server_fd);
handle_request(sock_ptr);
exit(0);
}
else
{
close(client_fd);
waitpid(-1, NULL, WNOHANG);
}
}
else if (MULTI_THRD == method)
{
pthread_t tid;
pthread_create(&tid, NULL, handle_request, sock_ptr);
pthread_detach(tid);
}
}
}
close(server_fd);
return 0;
}