.
├── bin
│ ├── common.o
│ ├── epoll_serv
│ ├── epoll_serv.o
│ ├── process_serv
│ ├── process_serv.o
│ ├── pthread_serv
│ └── pthread_serv.o
├── include
│ └── common.h
├── Makefile
├── src
│ ├── common.c
│ ├── epoll_serv.c
│ ├── process_serv.c
│ └── pthread_serv.c
└── web
├── About.html
├── Contact.html
├── css
│ ├── reset.css
│ ├── style.css
│ └── style_mobile.css
├── data
│ └── contact.json
├── images
│ ├── About_baby.png
│ ├── About_ball.png
│ ├── About_be.png
│ ├── About_bird.png
│ ├── About_content.png
│ ├── About_message.png
│ ├── About_@.png
│ ├── body.png
│ ├── footer_baby.png
│ ├── footer_ball.png
│ ├── footer_be.png
│ ├── footer_bird.png
│ ├── footer_message.png
│ ├── footer_@.png
│ ├── footer.png
│ ├── header.png
│ └── Thumbs.db
├── img
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ ├── 5.jpg
│ ├── 6.jpg
│ └── Thumbs.db
├── Index.html
├── js
│ ├── action.js
│ └── jquery-1.9.1.min.js
├── test.html
└── Work.html
以上是我的文件目录数,web文件里面的内容是实验提供的,
├── include
│ └── common.h
├── Makefile
├── src
│ ├── common.c
│ ├── epoll_serv.c
│ ├── process_serv.c
│ └── pthread_serv.c
这几个是我自己写的,代码如下:
common.h
#ifndef __COMMON_H__
#define __COMMON_H__
#define _XOPEN_SOURCE 700 // 消除sigaction相关报错
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 标准库函数
#include <string.h> // 字符串处理
#include <unistd.h> // UNIX标准函数
#include <sys/socket.h> // 套接字接口
#include <netinet/in.h> // 网络地址结构
#include <arpa/inet.h> // 地址转换函数
#include <fcntl.h> // 文件控制
#include <sys/stat.h> // 文件状态
#include <sys/sendfile.h> // 高效文件传输
#include <sys/types.h> // 系统类型
#include <dirent.h> // 目录操作
#include <errno.h> // 错误码
#include <regex.h> // 正则表达式
#include <sys/wait.h> // 关闭子进程
#include <signal.h> // 信号处理
#include <sys/epoll.h> // epoll I/O多路复用
#include <pthread.h> // 多线程头文件
#include <semaphore.h> // 信号量
#define PORT 80 // 监听端口
#define MAX_EVENTS 1024 // epoll最大事件数
#define BUFFER_SIZE 4096 // 请求缓冲区大小
#define ROOT_DIR "./web" // Web根目录,根据执行可执行文件的路径来设置
#define MAX_MATCHES 30 // 最大匹配数
/* 根据文件扩展名获取MIME类型 */
const char *get_mime_type(const char *path);
/* 发送HTTP响应 */
void send_response(int fd, int status, const char *status_msg,
const char *content_type, const char *body, int body_len);
/* 发送文件内容 */
void send_file(int fd, const char *path);
/* 处理GET请求 */
void handle_get_request(int fd, const char *path);
/* 处理POST请求 */
void handle_post_request(int fd, const char *path, const char *body, int body_len);
/* 处理HTTP请求 */
void handle_request(int fd, const char *request);
#endif
process_serv.c
#include "common.h"
#define WORKER_PROCESSES 1 /* 工作进程数量 */
/*
信号处理函数,用于销毁子进程
*/
void read_childproc(int sig)
{
/* 入参检查*/
if (sig < 0)
{
return;
}
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
if (WIFEXITED(status))
{
printf("Normal removed proc id: %d\n", pid);
}
else
{
printf("Abnormal removed proc id: %d\n", pid);
}
}
}
/*
进程工作
*/
void worker_process(int server_fd)
{
/* 入参检查*/
if (server_fd <= 2)
{
return;
}
while (1)
{
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen);
if (client_fd < 0)
{
if (errno == EINTR)
continue; /* 被信号中断,重试 */
perror("accept");
continue;
}
printf("[Worker %d] New connection: %s:%d\n",
getpid(), inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
/* 读取请求 */
char buffer[BUFFER_SIZE];
ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read <= 0)
{
close(client_fd);
continue;
}
buffer[bytes_read] = '\0';
fprintf(stdout, "======buffer======\n%s============\nclient_fd:%d\n", buffer, client_fd);
/* 处理请求 */
handle_request(client_fd, buffer);
/* 关闭连接 */
close(client_fd);
}
}
/**
* 主函数:设置服务器并创建工作进程
*/
int main()
{
int server_fd;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
/* 创建套接字 */
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
/* 设置套接字选项(允许地址重用) */
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
{
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 绑定地址 */
memset(&addr, 0, addrlen);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&addr, addrlen) < 0)
{
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 开始监听 */
if (listen(server_fd, SOMAXCONN) < 0)
{
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Web server running on port %d...\n", PORT);
printf("Root directory: %s\n", ROOT_DIR);
/* 设置信号处理 */
struct sigaction act;
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
/* 注册信号 */
if (sigaction(SIGCHLD, &act, 0) < 0)
{
perror("sigaction failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 创建工作进程 */
for (int i = 0; i < WORKER_PROCESSES; i++)
{
pid_t pid = fork();
if (pid == 0)
{ /* 子进程 */
worker_process(server_fd);
exit(EXIT_SUCCESS);
}
else if (pid < 0)
{
perror("fork failed");
exit(EXIT_FAILURE);
}
}
/* 主进程等待 */
while (1)
{
pause(); /* 等待信号 */
}
close(server_fd);
return 0;
}
epoll_serv.c
#include "common.h"
/* 客户端数据结构 */
typedef struct
{
int fd; /* 客户端套接字描述符 */
char buffer[BUFFER_SIZE]; /* 请求数据缓冲区 */
int buffer_len; /* 当前缓冲区数据长度 */
int status; /* 客户端状态(0:等待,1:读取中,2:写入中) */
} client_data;
/**
* 工作进程函数(处理客户端连接)
*/
void worker_process(int server_fd)
{
/* 入参检查*/
if (server_fd <= 2)
{
return;
}
while (1)
{
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen);
if (client_fd < 0)
{
if (errno == EINTR)
continue; /* 被信号中断,重试 */
perror("accept");
continue;
}
printf("[Worker %d] New connection: %s:%d\n",
getpid(), inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
/* 读取请求 */
char buffer[BUFFER_SIZE];
ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read <= 0)
{
close(client_fd);
continue;
}
buffer[bytes_read] = '\0';
fprintf(stdout, "======buffer======\n%s============\n", buffer);
/* 处理请求 */
handle_request(client_fd, buffer);
/* 关闭连接 */
close(client_fd);
}
}
/**
* 主函数:设置服务器并运行事件循环
*/
int main()
{
int server_fd, epoll_fd;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
/* 1. 创建服务器套接字 */
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
/* 设置套接字选项(允许地址重用) */
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
{
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 2. 配置服务器地址 */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; /* 监听所有网络接口 */
addr.sin_port = htons(PORT); /* 监听端口 */
/* 3. 绑定套接字 */
if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 4. 开始监听 */
if (listen(server_fd, SOMAXCONN) < 0)
{
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Web server running on port %d...\n", PORT);
printf("Root directory: %s\n", ROOT_DIR);
/* 5. 创建epoll实例 */
if ((epoll_fd = epoll_create1(0)) < 0)
{
perror("epoll_create1 failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 6. 添加服务器套接字到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 failed");
close(server_fd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
printf("Web server running on port %d...\n", PORT);
printf("Root directory: %s\n", ROOT_DIR);
/* 7. 分配客户端数据存储空间 */
struct epoll_event events[MAX_EVENTS];
client_data *clients = calloc(MAX_EVENTS, sizeof(client_data));
/* 8. 主事件循环 */
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++)
{
int fd = events[i].data.fd;
/* 处理新连接 */
if (fd == server_fd)
{
/* 接受新连接 */
int client_fd = accept(server_fd, (struct sockaddr *)&addr, &addrlen);
if (client_fd < 0)
{
perror("accept");
continue;
}
/* 设置非阻塞模式 */
fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL, 0) | O_NONBLOCK);
/* 添加新客户端到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");
close(client_fd);
}
/* 初始化客户端数据 */
clients[client_fd].fd = client_fd;
clients[client_fd].buffer_len = 0;
clients[client_fd].status = 0;
printf("New connection: %s:%d\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
else
{
/* 处理现有客户端数据 */
client_data *client = &clients[fd];
/* 读取客户端数据 */
int bytes_read = read(fd, client->buffer + client->buffer_len,
BUFFER_SIZE - client->buffer_len);
if (bytes_read <= 0)
{
/* 连接关闭或错误 */
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
memset(client, 0, sizeof(client_data));
continue;
}
/* 更新缓冲区 */
client->buffer_len += bytes_read;
client->buffer[client->buffer_len] = '\0';
/* 检查是否收到完整HTTP请求(以空行结束) */
if (strstr(client->buffer, "\r\n\r\n") != NULL)
{
/* 处理请求 */
handle_request(fd, client->buffer);
/* 重置缓冲区 */
client->buffer_len = 0;
memset(client->buffer, 0, BUFFER_SIZE);
}
}
}
}
/* 清理资源(实际不会执行到这里) */
free(clients);
close(server_fd);
close(epoll_fd);
return 0;
}
pthread.c
#include "common.h"
#include <pthread.h>
#include <errno.h>
/*
线程工作函数
*/
void *worker_pthread(void *arg)
{
/* 入参检查*/
if (NULL == arg)
{
return NULL;
}
/* 从参数中获取客户端文件描述符 */
int client_fd = *(int *)arg;
free(arg); /* 释放动态分配的内存 */
printf("------------------------- work process ----------------------------\n");
char buffer[BUFFER_SIZE];
ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read < 0)
{
perror("read error");
close(client_fd);
return NULL;
}
buffer[bytes_read] = '\0'; /* 确保字符串正确终止 */
/* 修复格式字符串:参数顺序应与格式匹配 */
fprintf(stdout, "======buffer======\nclient_fd:%d\t\n%s============\n", client_fd, buffer);
/*
======buffer======
client_fd:5
GET /images/footer.png HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept:
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost/css/style_mobile.css
Connection: keep-alive
============
*/
/* 处理请求 */
if (strlen(buffer) > 0)
{
handle_request(client_fd, buffer);
}
/* 关闭连接 */
close(client_fd);
printf("______________________________________________________\n");
return NULL;
}
/**
* 主函数:设置服务器并创建回收线程
*/
int main()
{
int server_fd;
struct sockaddr_in addr, client_addr;
socklen_t client_len = sizeof(client_addr);
/* 创建套接字 */
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket creation failed");
exit(EXIT_FAILURE);
}
/* 设置套接字选项(允许地址重用) */
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
{
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 绑定地址 */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
/* 开始监听 */
if (listen(server_fd, SOMAXCONN) < 0)
{
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Web server running on port %d...\n", PORT);
printf("Root directory: %s\n", ROOT_DIR);
while (1)
{
/* 接受新连接 */
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0)
{
perror("accept failed");
continue;
}
/* 动态分配内存传递client_fd(避免线程间竞争) */
int *client_ptr = malloc(sizeof(int));
if (!client_ptr)
{
perror("malloc failed");
close(client_fd);
continue;
}
*client_ptr = client_fd;
/* 创建新线程处理连接 */
pthread_t t_id;
if (pthread_create(&t_id, NULL, worker_pthread, client_ptr) != 0)
{
perror("pthread_create failed");
free(client_ptr);
close(client_fd);
continue;
}
/* 分离线程(自动回收资源) */
pthread_detach(t_id);
printf("New connection: %s:%d (thread %lu)\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),
(unsigned long)t_id);
}
close(server_fd);
return 0;
}
实验要求如下,帮我写一个实验报告