按照上面的debug机制,将下面两个程序也运用进去,程序1:
/* !Copyright(c) 2022-2025 TP-LINK Technologies CO.LTD.
* All rights reserved.
*
* \file server_process.c
* \brief This source file is for testing code.
*
* \author zhaoyunlong <zhaoyunlong1@tp-link.com.hk>
* \version 1.0.0
* \date 07/8/2025
*
* \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file.
*/
#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 <sys/wait.h>
#include <signal.h>
#include "http_parser.h"
#include "http_response.h"
#include "utils.h"
#define PORT 80 /* 服务器监听端口 */
#define BACKLOG 128 /* 等待连接队伍的最大长度 */
#define BUFFER_SIZE 8192 /* 接收http请求的缓冲区大小(8KB) */
// 客户端处理函数
void handle_client(void *arg)
{
int client_fd = *(int *)arg;
char buffer[BUFFER_SIZE];
char client_ip[INET_ADDRSTRLEN];
// 获取客户端IP
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len); /* 获取与套接字关联的远程协议地址 */
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); /* 将数值格式的IP地址转换为点分十进制格式 */
// 接收HTTP请求
ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); /* 等待接收数据 */
if (bytes_read <= 0)
{
close(client_fd);
free(arg);
return;
}
buffer[bytes_read] = '\0';
// 解析HTTP请求
http_request_t req;
if (parse_http_request(buffer, &req) != 0)
{
log_request(client_ip, "UNKNOWN", "INVALID", 400);
send_error_response(client_fd, 400, "Bad Request");
close(client_fd);
free(arg);
return;
}
// 记录请求日志
log_request(client_ip, req.method, req.path, 200);
// 处理请求
if ((strcmp(req.method, "GET") == 0) || (strcmp(req.method, "POST") == 0))
{
handle_http_request(client_fd, &req);
}
else
{
send_error_response(client_fd, 501, "Not Implemented");
}
close(client_fd);
free(arg);
}
// SIGCHLD信号处理函数(回收僵尸进程)
void sigchld_handler(int sig)
{
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
int main()
{
int server_fd;
int client_fd;
struct sockaddr_in server_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)))
{
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 绑定地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 开始监听
if (listen(server_fd, BACKLOG) < 0)
{
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 设置SIGCHLD信号处理僵尸进程
signal(SIGCHLD, sigchld_handler); // 安装信号处理器
printf("Web server running on port %d\n", PORT);
while (1)
{
// 接受新连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0)
{
perror("accept failed");
continue;
}
// 分配客户端套接字内存
int *client_ptr = malloc(sizeof(int));
if (!client_ptr)
{
perror("malloc failed");
close(client_fd);
continue;
}
*client_ptr = client_fd;
/* 多进程处理 */
// 创建子进程处理连接
pid_t pid = fork();
if (pid == 0)
{ // 子进程
close(server_fd); // 关闭监听套接字
handle_client(client_ptr);
exit(EXIT_SUCCESS); // 处理完成后退出
}
else if (pid > 0)
{ // 父进程
close(client_fd); // 关闭客户端套接字(父进程不需要)
}
else
{
perror("fork failed");
close(client_fd);
}
}
// 清理资源
close(server_fd);
return 0;
}
程序2:
/* !Copyright(c) 2022-2025 TP-LINK Technologies CO.LTD.
* All rights reserved.
*
* \file server_epoll.c
* \brief This source file is for testing code.
*
* \author zhaoyunlong <zhaoyunlong1@tp-link.com.hk>
* \version 1.0.0
* \date 07/8/2025
*
* \history \arg 1.0.0, 07/8/2025, zhaoyunlong, Create file.
*/
#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 <sys/wait.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include "http_parser.h"
#include "http_response.h"
#include "utils.h"
#define PORT 80 /* 服务器监听端口 */
#define BACKLOG 128 /* 等待连接队伍的最大长度 */
#define MAX_EVENTS 1024 /* epoll最大事件数 */
#define BUFFER_SIZE 8192 /* 接收http请求的缓冲区大小(8KB) */
// 客户端连接信息结构体
typedef struct
{
int fd; // 客户端文件描述符
char buffer[BUFFER_SIZE]; // 接收缓冲区
ssize_t bytes_received; // 已接收字节数
char client_ip[INET_ADDRSTRLEN]; // 客户端IP地址
} client_info_t;
// 处理客户端请求
void process_client(int client_fd, client_info_t *client)
{
http_request_t req;
// 解析HTTP请求
if (parse_http_request(client->buffer, &req) != 0)
{
// 解析失败记录错误日志
log_request(client->client_ip, "UNKNOWN", "INVALID", 400);
send_error_response(client_fd, 400, "Bad Request");
return;
}
// 记录成功请求日志
log_request(client->client_ip, req.method, req.path, 200);
// 处理GET/POST请求
if (strcmp(req.method, "GET") == 0 || strcmp(req.method, "POST") == 0)
{
handle_http_request(client_fd, &req);
}
else
{
// 不支持的HTTP方法
send_error_response(client_fd, 501, "Not Implemented");
}
}
int main()
{
int server_fd;
// int client_fd;
int epoll_fd;
struct sockaddr_in server_addr;
struct epoll_event ev, events[MAX_EVENTS]; // epoll事件结构
// 创建套接字
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)))
{
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 绑定地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 开始监听
if (listen(server_fd, BACKLOG) < 0)
{
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 创建epoll实例
if ((epoll_fd = epoll_create1(0)) < 0)
{
perror("epoll_create1 failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 添加服务器套接字到epoll监听
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 failed");
close(server_fd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
printf("Web server running on port %d\n", PORT);
while (1)
{
// 等待事件发生,-1表示无限等待
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds < 0)
{
perror("epoll_wait failed");
continue;
}
// 处理所有就绪事件
for (int i = 0; i < nfds; i++)
{
// 处理新客户端连接
if (events[i].data.fd == server_fd)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
// 接受新连接
int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0)
{
perror("accept failed");
continue;
}
// 设置非阻塞模式(边缘触发必须)
int flags = fcntl(client_fd, F_GETFL, 0);
fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
// 创建并初始化客户端信息结构
client_info_t *client = malloc(sizeof(client_info_t));
if (!client)
{
perror("malloc client_info failed");
close(client_fd);
continue;
}
memset(client, 0, sizeof(client_info_t));
client->fd = client_fd;
// 获取客户端IP
inet_ntop(AF_INET, &client_addr.sin_addr, client->client_ip, INET_ADDRSTRLEN);
// 添加客户端到epoll监听(边缘触发模式)
ev.events = EPOLLIN | EPOLLET; // 边缘触发模式
ev.data.ptr = client; // 关联客户端数据结构
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0)
{
perror("epoll_ctl client_fd failed");
close(client_fd);
free(client);
}
}
// 处理客户端数据可读事件
else if (events[i].events & EPOLLIN)
{
client_info_t *client = (client_info_t *)events[i].data.ptr;
// 非阻塞读取数据
ssize_t count = recv(client->fd,
client->buffer + client->bytes_received,
BUFFER_SIZE - client->bytes_received - 1, 0);
if (count > 0)
{
// 更新已接收字节数
client->bytes_received += count;
client->buffer[client->bytes_received] = '\0'; // 确保字符串结束
// 检查HTTP请求是否结束(通过空行判断)
if (strstr(client->buffer, "\r\n\r\n") != NULL)
{
// 移除epoll监听
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client->fd, NULL);
// 处理完整请求
process_client(client->fd, client);
// 关闭连接并释放资源
close(client->fd);
free(client);
}
}
// 处理连接关闭或错误
else if (count == 0 || (count < 0 && errno != EAGAIN))
{
// 移除epoll监听
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client->fd, NULL);
close(client->fd);
free(client);
}
// EAGAIN错误在边缘触发模式下是正常的,表示数据已读完
}
}
}
// 清理资源
close(epoll_fd);
close(server_fd);
return 0;
}
最新发布