#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 8888
#define WEB_ROOT "./web"
/**************************************************************************************************/
/**************************************************************************************************/
/* TYPES */
enum method_enum
{
MULTI_PROG,
MULTI_THRD,
MULTI_IO
};
/**************************************************************************************************/
/**************************************************************************************************/
/* EXTERN_PROTOTYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* LOCAL_PROTOTYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* VARIABLES */
int method = MULTI_IO;
/**************************************************************************************************/
/**************************************************************************************************/
/* LOCAL_FUNCTIONS */
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 */
/**************************************************************************************************/
/*
* 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;
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];
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;
if(MULTI_IO != method){
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);
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)
{
int epoll_fd = epoll_create1(0);
struct epoll_event ev, events[100];
ev.events = EPOLLIN;
ev.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);
while (1)
{
int n = epoll_wait(epoll_fd, events, 100, -1);
for (int i = 0; i < n; i++)
{
if (events[i].data.fd == server_fd)
{
int client_fd = accept(server_fd, NULL, NULL);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);
}
else
{
handle_request(&events[i].data.fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
close(events[i].data.fd);
}
}
}
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, server_fd, NULL);
}else{
while (1)
{
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd < 0)
{
perror("accept failed");
continue;
}
int *sock_ptr = malloc(sizeof(int));
if (!sock_ptr)
{
close(client_fd);
continue;
}
*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;
}
如何优化代码使得其具有高并发性
最新发布