/* Copyright(c) 2008-2024 ChengDu TP-LINK Technologies Co.Ltd.
*
* file multi_thread.c
* brief Construct a webserver using a multi-thread approach
* author Wang Junhang
* version 0.0.0
* date 2025-08-07
*
* history
*/
/************************************Include Files**********************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
/***********************************************************************************/
/* DEFINES */
/***********************************************************************************/
#define PORT 8080 /* Socket Port */
#define WEB_ROOT "./www" /* Server root directory */
#define BUFFER_SIZE 1024
#define MAX_SOCKET_CONN 10 /*Maximum number of socket connections*/
#define LOG_FILE_NAME "multi_thread.log"
/***********************************************************************************/
/* TYPES */
/***********************************************************************************/
/***********************************************************************************/
/* EXTERN_PROTOTYPES */
/***********************************************************************************/
/***********************************************************************************/
/* LOCAL_PROTOTYPES */
/***********************************************************************************/
/***********************************************************************************/
/* VARIABLES */
/***********************************************************************************/
FILE *log_file; /* Global variable for log file pointer */
int server_socket; /* Global server socket file descriptor */
time_t now; /* Global time */
struct tm *tm_info;
/***********************************************************************************/
/* LOCAL_FUNCTIONS */
/***********************************************************************************/
/***********************************************************************************/
/* PUBLIC_FUNCTIONS */
/***********************************************************************************/
void *handle_request(void *socket_ptr);
void send_response_header(int socket, int status, char *content_type);
void init_log(const char *log_filename);
void close_log();
void signal_handler(int sig);
void cleanup();
/***********************************************************************************/
/* GLOBAL_FUNCTIONS */
/***********************************************************************************/
int main()
{
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGTSTP, signal_handler);
init_log(LOG_FILE_NAME);
int client_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
/* Forcefully attaching socket to the defined port */
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_socket, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("Bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_socket, MAX_SOCKET_CONN) < 0)
{
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d\n", PORT);
fprintf(log_file, "Server listening on port %d\n\r\n", PORT); /* recording start to log */
/* Accept incoming connections and handle in separate threads */
while (1)
{
if ((client_socket = accept(server_socket, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0)
{
perror("Accept failed");
exit(EXIT_FAILURE);
}
/* Create a new thread to handle the client */
pthread_t thread;
int *socket_ptr = malloc(sizeof(int));
*socket_ptr = client_socket;
if (pthread_create(&thread, NULL, handle_request, (void *)socket_ptr) < 0)
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_detach(thread); /* Detach the thread to avoid memory leaks */
}
cleanup();
return 0;
}
/*
* fn void *handle_request(void *socket_ptr)
* brief Respond to HTTP requests based on different request methods (GET/POST/OTHERS).
* details
* param[in] socket_ptr The pointer of connected socket
*
* return NULL
* retval NULL
*
* note
*/
void *handle_request(void *socket_ptr)
{
int client_socket = *((int *)socket_ptr);
free(socket_ptr); /* release the pointer of client socket*/
int read_size;
char buffer[BUFFER_SIZE] = {0};
char method[10], path[255], protocol[20];
read_size = read(client_socket, buffer, sizeof(buffer));
if (read_size <= 0)
{
perror("Read failed");
return;
}
fprintf(log_file, "Request received:\n%s\r\n", buffer); /* recording request to log */
if (strstr(buffer, "GET") != NULL)
{
sscanf(buffer, "%s %s %s", method, path, protocol);
printf("Received request:\n%s\n", buffer);
if (strcmp(path, "/") == 0)
{
char tmp_path_1[64], tmp_path_2[64];
snprintf(tmp_path_1, sizeof(tmp_path_1), "%s%s", WEB_ROOT, "/index.html");
snprintf(tmp_path_2, sizeof(tmp_path_1), "%s%s", WEB_ROOT, "/Index.html");
if (access(tmp_path_1, F_OK) != -1)
{
sprintf(path, "/index.html");
}
else if (access(tmp_path_2, F_OK) != -1)
{
sprintf(path, "/Index.html");
}
else
{
}
}
/* Construct full file path (prepend ./www) */
char full_path[256];
snprintf(full_path, sizeof(full_path), "%s%s", WEB_ROOT, path);
int file_fd = open(full_path, O_RDONLY);
if (file_fd == -1)
{
fprintf(log_file, "Error opening file: %s\r\n", full_path);
char *content_type = "text/html";
char *content = "<h1>404 Not Found</h1>";
send_response_header(client_socket, 404, content_type);
send(client_socket, content, strlen(content), 0);
}
else
{
off_t file_size = lseek(file_fd, 0, SEEK_END);
lseek(file_fd, 0, SEEK_SET);
printf("Content is:%d:\r\n", file_size);
char *content_type = "text/html";
if (strstr(full_path, ".png"))
content_type = "image/png";
if (strstr(full_path, ".jpg"))
content_type = "image/jpeg";
if (strstr(full_path, ".gif"))
content_type = "image/gif";
if (strstr(full_path, ".html"))
content_type = "text/html";
if (strstr(full_path, ".css"))
content_type = "text/css";
if (strstr(full_path, ".json"))
content_type = "text/javascript";
if (strstr(full_path, ".js"))
content_type = "text/javascript";
send_response_header(client_socket, 200, content_type);
/* Send response content */
char send_buffer[BUFFER_SIZE];
ssize_t bytes_read;
while ((bytes_read = read(file_fd, send_buffer, BUFFER_SIZE)) > 0)
{
write(client_socket, send_buffer, bytes_read);
}
close(file_fd);
fprintf(log_file, "Response file: %s\r\n", full_path);
}
}
else if (strstr(buffer, "POST") != NULL)
{
/* Handle POST requests */
char *content_length_header = strstr(buffer, "Content-Length:");
int content_length = 0;
if (content_length_header != NULL)
{
content_length = atoi(content_length_header + 15); // Skip "Content-Length:" to get the value
}
/* Read POST data */
char post_content_buffer[BUFFER_SIZE] = {0};
int total_read = 0;
while ((read_size = recv(client_socket, post_content_buffer, 1024, 0)) > 0)
{
total_read += read_size;
if (total_read >= content_length)
{
break;
}
}
printf("Post data is:\r\n%s:\r\n\r\n", post_content_buffer);
fprintf(log_file, "Post data is: %s\r\n", post_content_buffer);
/* Process the received data (in buffer)
Here, you can handle the POST data according to your application's logic
For simplicity, we just send a response indicating the data was received*/
/* Send Response for POST request */
char *content_type = "text/html";
char *content = "<h1> Post Response</h1>";
send_response_header(client_socket, 200, content_type);
send(client_socket, content, strlen(content), 0);
}
else
{
/* Send Error Response */
char *content_type = "text/html";
char *content = "<h1>500 Internal Server Error</h1>";
send_response_header(client_socket, 500, content_type);
send(client_socket, content, strlen(content), 0);
fprintf(log_file, "500 Sever can't serve this type of request\r\n"); /* recording error to log */
}
close(client_socket); /* close the client socket file describer*/
return NULL;
}
/*
* fn void send_response_header(int socket, int status, char *content_type)
* brief Send the response header to the client
* details
* param[in] socket The file describer of connected socket
* param[in] status Http response status
* param[in] content_type The type of response
*
* param[out] None None
*
* return None
* retval None None
*
* note
*/
void send_response_header(int socket, int status, char *content_type)
{
char response[BUFFER_SIZE] = {0};
char headers[BUFFER_SIZE] = {0};
sprintf(headers, "HTTP/1.1 %d ", status);
switch (status)
{
case 200:
strcat(headers, "OK\r\n");
break;
case 404:
strcat(headers, "Not Found\r\n");
break;
default:
strcat(headers, "Internal Server Error\r\n");
}
/* Send HTTP response header */
sprintf(response, "%sContent-Type: %s\r\n\r\n", headers, content_type);
send(socket, response, strlen(response), 0);
}
/*
* brief init the log file
*/
void init_log(const char *log_filename)
{
log_file = fopen(log_filename, "a");
if (log_file == NULL)
{
perror("Error opening log file");
exit(EXIT_FAILURE);
}
time(&now);
tm_info = localtime(&now);
fprintf(log_file, "=== Server started ===\n");
fprintf(log_file, "Current time: %s", asctime(tm_info));
}
/*
* brief close the log file
*/
void close_log()
{
time(&now);
tm_info = localtime(&now);
fprintf(log_file, "Current time: %s", asctime(tm_info));
fprintf(log_file, "=== Server closed ===\n\r\n");
fclose(log_file);
}
/*
* brief process exit signal
*/
void signal_handler(int sig)
{
printf("\rCaught signal %d, exiting...\n", sig);
fprintf(log_file, "Caught signal %d, exiting...\n", sig);
cleanup();
exit(0);
}
/*
* brief clean all resources
*/
void cleanup()
{
close(server_socket);
close_log();
}
我的代码在接收到get请求时,服务器正常打印数据了,但是没有记录到日志文件中,为什么?
最新发布