#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#define PORT 8080
#define MAX_PATH 1024
#define BUFFER_SIZE 4096
#define MAX_EVENTS 64
/* 基础目录定义*/
const char *BASE_DIR = "./web";
/* URL路径映射函数*/
void map_url_to_path(char *url_path, char *file_path)
{
/* 默认路由处理*/
if (strcmp(url_path, "/") == 0)
{
snprintf(file_path, MAX_PATH, "%s/Index.html", BASE_DIR);
return;
}
/* 特殊目录映射*/
if (strncmp(url_path, "/images/", 8) == 0)
{
snprintf(file_path, MAX_PATH, "%s/images%s", BASE_DIR, url_path + 7);
return;
}
/* 通用文件映射*/
snprintf(file_path, MAX_PATH, "%s%s", BASE_DIR, url_path);
}
/* 获取MIME类型*/
const char *get_mime_type(const char *path)
{
const char *ext = strrchr(path, '.');
if (!ext)
return "text/plain";
if (strcmp(ext, ".html") == 0)
return "text/html";
if (strcmp(ext, ".css") == 0)
return "text/css";
if (strcmp(ext, ".js") == 0)
return "application/javascript";
if (strcmp(ext, ".jpg") == 0)
return "image/jpeg";
if (strcmp(ext, ".png") == 0)
return "image/png";
if (strcmp(ext, ".json") == 0)
return "application/json";
return "text/plain";
}
/* 处理HTTP请求*/
void *handle_request(int client_socket)
{
char buffer[BUFFER_SIZE];
read(client_socket, buffer, BUFFER_SIZE - 1);
/* 解析请求行*/
char *method = strtok(buffer, " ");
char *url_path = strtok(NULL, " ");
if (!url_path)
return NULL;
/* 映射到本地文件路径*/
char file_path[MAX_PATH];
map_url_to_path(url_path, file_path);
/* 检查文件是否存在*/
struct stat file_stat;
if (stat(file_path, &file_stat) < 0)
{
const char *not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
write(client_socket, not_found, strlen(not_found));
close(client_socket);
return NULL;
}
/* 处理目录请求(列出目录内容)*/
if (S_ISDIR(file_stat.st_mode))
{
DIR *dir = opendir(file_path);
if (!dir)
{
perror("opendir");
close(client_socket);
return NULL;
}
char dir_list[4096] = "<html><body><ul>";
struct dirent *entry;
while ((entry = readdir(dir)) != NULL)
{
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(dir_list + strlen(dir_list), sizeof(dir_list) - strlen(dir_list),
"<li><a href=\"%s\">%s</a></li>", entry->d_name, entry->d_name);
}
strcat(dir_list, "</ul></body></html>");
closedir(dir);
char header[512];
snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %ld\r\n\r\n",
strlen(dir_list));
write(client_socket, header, strlen(header));
write(client_socket, dir_list, strlen(dir_list));
close(client_socket);
return NULL;
}
/* 打开文件*/
int fd = open(file_path, O_RDONLY);
if (fd < 0)
{
perror("open");
close(client_socket);
return NULL;
}
/* 构建响应头*/
char header[512];
snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %ld\r\n\r\n",
get_mime_type(file_path), file_stat.st_size);
/* 发送响应*/
write(client_socket, header, strlen(header));
off_t offset = 0;
sendfile(client_socket, fd, &offset, file_stat.st_size);
close(fd);
close(client_socket);
return NULL;
}
/* 主函数(多线程)*/
int main()
{
int server_fd;
struct sockaddr_in address;
int addrlen = sizeof(address);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
/*设置套接字选项*/
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/* 创建套接字*/
if (server_fd == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
/* 配置服务器地址*/
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
/* 绑定端口*/
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
/* 开始监听*/
if (listen(server_fd, 10) < 0)
{
perror("listen failed");
exit(EXIT_FAILURE);
}
printf("Web server running at http://localhost:%d\n", PORT);
/*创建epoll实例*/
int epoll_fd = epoll_create1(0);
if (epoll_fd < 0)
{
perror("epoll_createl failed");
close(server_fd);
eixt(EXIT_FAILURE);
}
/*添加服务器套接字到epoll*/
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0)
{
perror("epoll_ctl: server_fd");
close(server_fd);
close(epoll_fd);
eixt(EXIT_FAILURE);
}
struct epoll_event events[MAX_EVENTS];
while (1)
{
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds < 0)
{
perror("epoll_wait");
continue;
}
for (int i = 0; i < nfds; i++)
{
if (events[i].data.fd == server_fd)
{
/* 处理新连接*/
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_fd = accept4(server_fd, (struct sockaddr *)&client_addr,
&addr_len, SOCK_NONBLOCK);
if (client_fd < 0)
{
perror("accept4 failure");
continue;
}
printf("New connection from %s:%d\n", inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
/*添加新客户端到epoll*/
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0)
{
perror("epoll_ctl: client_fd failed");
close(client_fd);
}
}
else
{
/*处理客户端需求*/
handle_request(events[i].data.fd);
}
}
}
close(server_fd);
close(epoll_fd);
return 0;
}
多路复用代码,还需要修改你帮我改一下主函数里面的使他能运行起来