实验环境:centos7虚拟机环境
我有4个html文件,文件中可能包含图片等内容,前端文件如下
.
├── 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
├── webserv_linux
├── webserv_linux.c
└── Work.html
请使用多进程的方式实现一个简单的web server(端口80),使得我能够在浏览器实现对各个html网页的访问。
浏览器兼容要求:Firefox,chrome、ie8+
Web server需完成get post等基础请求处理(必修)
使用makefile构建(必修)
我想利用多进程实现server客户端,代码如下,帮我检查修改main函数
#define _XOPEN_SOURCE // 消除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 <sys/epoll.h> // epoll I/O多路复用
#include <fcntl.h> // 文件控制
#include <sys/stat.h> // 文件状态
#include <sys/sendfile.h> // 高效文件传输
#include <sys/types.h> // 系统类型
#include <dirent.h> // 目录操作
#include <errno.h> // 错误码
#include <sys/wait.h> // 关闭子进程
#include <signal.h> // 信号处理
#define PORT 80 // 监听端口
#define MAX_EVENTS 1024 // epoll最大事件数
#define BUFFER_SIZE 4096 // 请求缓冲区大小
#define ROOT_DIR “./web/” // Web根目录
typedef struct
{
int fd;
char buffer[BUFFER_SIZE];
int buffer_len;
int status;
} client_data;
/*
信号处理函数,用于销毁子进程
*/
void read_childproc(int sig)
{
pid_t pid;
int status;
pid = waitpid(-1,&status,WNOHANG);
if(WIFEIXITED(&status))
{
printf(“normal removed proc id: %d\n”,pid);
}
else
{
printf(“abnormal removed proc id: %d\n”,pid);
}
}
/**
根据文件扩展名获取MIME类型
@param path 文件路径
@return 对应的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, “.htm”) == 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, “.jpeg”) == 0)
return “image/jpeg”;
if (strcmp(ext, “.png”) == 0)
return “image/png”;
if (strcmp(ext, “.gif”) == 0)
return “image/gif”;
if (strcmp(ext, “.json”) == 0)
return “application/json”;
if (strcmp(ext, “.ico”) == 0)
return “image/x-icon”;
return “text/plain”; // 未知扩展名默认纯文本
}
/**
发送HTTP响应
@param fd 客户端套接字
@param status HTTP状态码
@param status_msg 状态描述
@param content_type 内容类型
@param body 响应体内容
@param body_len 响应体长度
*/
void send_response(int fd, int status, const char *status_msg,
const char *content_type, const char *body, int body_len)
{
char header[BUFFER_SIZE];
// 构建HTTP响应头
int header_len = snprintf(header, BUFFER_SIZE,
“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);
// 发送响应头
write(fd, header, header_len);
// 发送响应体(如果有)
if (body && body_len > 0)
{
write(fd, body, body_len);
}
}
/**
发送文件内容
@param fd 客户端套接字
@param path 文件路径
*/
void send_file(int fd, const char *path)
{
struct stat file_stat;
// 获取文件状态信息
if (stat(path, &file_stat) < 0 || !S_ISREG(file_stat.st_mode))
{
// 文件不存在或不是普通文件
send_response(fd, 404, “Not Found”, “text/html”,
“
404 Not Found”, 22);
return;
}
// 打开文件
int file_fd = open(path, O_RDONLY);
if (file_fd < 0)
{
// 文件打开失败
send_response(fd, 500, “Internal Server Error”, “text/html”,
“
500 Internal Error”, 26);
return;
}
// 获取MIME类型
const char *mime_type = get_mime_type(path);
fprintf(stdout, “mime_type:\n%s\n”, mime_type);
char header[BUFFER_SIZE];
// 构建成功响应头
int header_len = snprintf(header, BUFFER_SIZE,
“HTTP/1.1 200 OK\r\n”
“Content-Type: %s\r\n”
“Content-Length: %ld\r\n”
“Connection: close\r\n”
“\r\n”,
mime_type, file_stat.st_size);
// 发送响应头
write(fd, header, header_len);
// 使用sendfile零拷贝发送文件内容
sendfile(fd, file_fd, NULL, file_stat.st_size);
// 关闭文件
close(file_fd);
}
/**
处理GET请求
@param fd 客户端套接字
@param path 请求路径
*/
void handle_get_request(int fd, const char *path)
{
char full_path[BUFFER_SIZE];
// 构建完整文件路径
snprintf(full_path, BUFFER_SIZE, “%s%s”, ROOT_DIR, path);
// 路径安全检测(防止路径遍历攻击)
if (strstr(path, “…/”) || strstr(path, “…\”))
{
send_response(fd, 403, “Forbidden”, “text/html”,
“
403 Forbidden”, 22);
return;
}
// 处理目录请求(以’/'结尾)
if (path[strlen(path) - 1] == ‘/’)
{
DIR *dir = opendir(full_path);
if (!dir)
{
// 目录打开失败
send_response(fd, 404, “Not Found”, “text/html”,
“
404 Not Found”, 22);
return;
}
// 生成目录列表HTML char body[BUFFER_SIZE * 2] = "<html><head><title>Index of "; strcat(body, path); strcat(body, "</title></head><body><h1>Index of "); strcat(body, path); strcat(body, "</h1><hr><ul>"); struct dirent *entry; // 遍历目录项 while ((entry = readdir(dir)) != NULL) { // 跳过.和.. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; char li[256]; // 构建列表项 snprintf(li, sizeof(li), "<li><a href=\"%s%s\">%s</a></li>", path, entry->d_name, entry->d_name); strcat(body, li); } strcat(body, "</ul><hr></body></html>"); // 发送目录列表 send_response(fd, 200, "OK", "text/html", body, strlen(body)); closedir(dir); return;
}
// 发送文件内容
send_file(fd, full_path);
}
/**
处理POST请求
@param fd 客户端套接字
@param path 请求路径
@param body 请求体内容
@param body_len 请求体长度
*/
void handle_post_request(int fd, const char *path, const char *body, int body_len)
{
// 示例:处理联系表单提交
if (strcmp(path, “/Contact.html”) == 0)
{
// 在实际应用中应验证和存储数据
printf(“Received form data: %.*s\n”, body_len, body);
// 返回成功响应
send_response(fd, 200, “OK”, “text/html”,
“
Thank You!
Form submitted successfully
”, 60);
}
else
{
// 不支持其他POST路径
send_response(fd, 404, “Not Found”, “text/html”, “
404 Not Found”, 22);
}
}
/**
处理HTTP请求
@param fd 客户端套接字
@param request 请求数据
*/
void handle_request(int fd, const char *request)
{
char method[16], path[256], protocol[16];
// 解析请求行
sscanf(request, “%s %s %s”, method, path, protocol);
// fprintf(stdout,“%s\n%s\n %s\n”,method, path, protocol);
// 处理根路径请求
if (strcmp(path, “/”) == 0)
{
strcpy(path, “/Index.html”); // 默认返回主页
}
// 查找请求体起始位置
const char *body = strstr(request, “\r\n\r\n”);
if (body)
body += 4; // 跳过空行
// 方法分发
if (strcmp(method, “GET”) == 0)
{
handle_get_request(fd, path);
}
else if (strcmp(method, “POST”) == 0)
{
handle_post_request(fd, path, body, (body ? strlen(body) : 0));
}
else
{
// 不支持的方法
send_response(fd, 501, “Not Implemented”, “text/html”,
“
501 Not Implemented”, 25);
}
}
/**
主函数:设置服务器并运行事件循环
*/
int main()
{
int server_fd;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
pid_t pid;
struct sigaction act;
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
int state = sigaction(SIGCHLD,&act,0);
//
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
}
memset(&server_fd,0,addrlen);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
// 设置套接字选项(允许地址重用)
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定套接字
if ((bind(server_fd, (struct sockaddr *)&addr, addrlen)) < 0)
{
}
// 4. 开始监听
if((listen(server_fd,SOMAXCONN)) < 0)
{
}
printf(“Web server running on port %d…\n”, PORT);
printf(“Root directory: %s\n”, ROOT_DIR);
// 7. 分配客户端数据存储空间
client_data *client = calloc(1, sizeof(client_data));
//
while (1)
{
int client_fd = accept(server_fd,(struct sockaddr *) &addr,addrlen);
if(client_fd < 0)
{
perror(“accept”);
continue;
}
else
{
printf(“New connection: %s:%d\n”,
inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
}
pid = fork(); if(pid < 0) { close(client_fd); continue; } if(0 == pid) // child process { // 读取客户端数据 int bytes_read = read(client_fd, client->buffer + client->buffer_len, BUFFER_SIZE - client->buffer_len); if(bytes_read <=0 ) { close(client_fd); continue; } // 更新缓冲区 client->buffer_len += bytes_read; client->buffer[client->buffer_len] = '\0'; if(strstr(client->buffer, "\r\n\r\n") != NULL) { // 处理请求 handle_request(client_fd,client->buffer); // 重置缓冲区 client->buffer_len = 0; memset(client->buffer, 0, BUFFER_SIZE); } } else // father process { close(client_fd); }
}
close(server_fd);
return 0;
}
最新发布