fork(), read(), write(), O_APPEND 使用

本文通过示例程序详细解析了Linux中的fork()函数,展示了如何创建子进程,并解释了其返回值。同时,介绍了execv()在子进程中替换当前进程映像的能力。接着,利用lseek()和O_APPEND标志讨论了文件定位与追加写入的特性。最后,展示了read()和write()函数在读写文件时的交互行为。

互应Lab03 fork(), read(), write(), O_APPEND 练习

运行示例程序

fork1.c

linux中fork()函数详解 - 学习记录园 - 博客园

fork调用仅被调用一次,能返回两次,可有三种返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;

创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。

两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){
    pid_t t;
    // fork is going to create a child process
    t=fork();
    printf("fork returned %d\n",t);
    // exit is to terminate a process and release all the resources
    exit(0);
}

在这里插入图片描述

fork2.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>


int main(void){

    pid_t t;

    printf("Original proram, pid = %d\n",getpid());
    t = fork();

    if(t==0){
        printf("In child process, pid = %d, ppid=%d\n",getpid(),getppid());
    }
    else{
        printf("In parent, pid = %d, fork returned = %d\n",
                getpid(),t);
    }
}

结果:

kunling@kunling-VirtualBox:~/Internet_Application/lab03$ ./fork2.out 
Original proram, pid = 3921
In parent, pid = 3921, ppid=3893, fork returned = 3922
kunling@kunling-VirtualBox:~/Internet_Application/lab03$ In child process, pid = 3922, ppid=1110
kunling@kunling-VirtualBox:~/Internet_Application/lab03$ gcc fork2.c -o fork2.out
kunling@kunling-VirtualBox:~/Internet_Application/lab03$ ./fork2.out 
Original proram, pid = 4041
In parent, pid = 4041, fork returned = 4042
kunling@kunling-VirtualBox:~/Internet_Application/lab03$ In child process, pid = 4042, ppid=1110

发现:

在虚拟机上创建新的进程和理论上是不一样的。这个问题需要探讨,可能是因为虚拟机请求主系统创建,那个pid是主系统的?

感觉Ubuntu打开终端老是要搞路径很麻烦,下载nautilus-open-terminal。

ubuntu14设置在当前目录打开终端_xbcReal的博客-优快云博客

exec1.c
#include<unistd.h>
#include<stdio.h>

int main (void){
    char *arg[] = {"/bin/ls",0};
    /* fork, and exec within child process*/
    if(fork()==0){
        printf("In child process:\n");
        execv(arg[0],arg);
        printf("I will never be called\n");
    }
    printf("Execution continues in parent process\n");
}

输出结果:

kunling@kunling-VirtualBox:~/Internet_Application/lab03$ ./exec1.out 
Execution continues in parent process

kunling@kunling-VirtualBox:~/Internet_Application/lab03$ In child process:
exec1.c   exec1.out  fork1.out    fork2.c~
exec1.c~  fork1.c    fork2.c    fork2.out
lseek1.c
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //exit()

char buf1[] ="abcdefghij";
char buf2[]="ABCDEFGHIJ";
#define FILE_MODE 0644


int main (void){
    int fd;

    if((fd = creat("file.hole",FILE_MODE))<0)
    {
        printf("creat error\n");

        exit(1);
    }
    if(write(fd,buf1,10)!=10){
        printf("buf1 write error\n");
        exit(1);
    }
    /*offset now = 10*/
    if(lseek(fd,40,SEEK_SET)==-1){
    printf("lseek error\n");
    exit(1);
    }
    /*offset now = 40*/
    if(write(fd,buf2,10)!=10){
    printf("buf2 write error\n");
    exit(1)}
    /*offset now = 50*/
    exit(0);
}

输出结果:

kunling@kunling-VirtualBox:~/Internet_Application/lab03$ od -c file.hole 
0000000   a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0   A   B   C   D   E   F   G   H
0000060   I   J
0000062
readwrite1.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(void){
        char quit='.';
        char buf[10];
        int fd;
        if((fd=open("out.out", O_RDWR| O_CREAT))==-1)
        printf("Error in opening\n");

        while(buf[0]!= quit) //遇到‘.'就结束!
        {
             read(0 ,buf, 1);
            write(fd,buf, 1);
            write(1 ,buf, 1);

        }
        close(fd);
}

输出结果:

root@kunling-VirtualBox:/home/kunling/Internet_Application/lab03# ./readwrite1.out 
1234567890
1234567890
qwertyuio
qwertyuio
ff.
ff.root@kunling-VirtualBox:/home/kunling/Internet_Application/lab03# 
root@kunling-VirtualBox:/home/kunling/Internet_Application/lab03# od -c out.out 0000000   1   2   3   4   5   6   7   8   9   0  \n   q   w   e   r   t
0000020   y   u   i   o  \n   f   f   .
0000030

验收O_APPEND的特性

lseek的与O_APPEND的冲突_大海蓝天的专栏-优快云博客

  • 读取特性

    按照lseek文件指针读取

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h> 

int main(){
    char rbuf1[100];
    int fd;
    int size;
    int currpos;

    //在当前目录创建或者打开文件
    fd = open("testing.txt",O_RDWR| O_APPEND);
    if(fd<0)
        printf("open failed");
    //移动文件指针到开头
    currpos = lseek(fd, 0 , SEEK_SET);

    if(currpos!=0){
    printf("lseek error");
    return -1;
    }

    if(read(fd,rbuf1,sizeof(rbuf1))<0)
    printf("read error!");

    //打印读取到的文件内容
    printf("%s",rbuf1);
    close(fd);
    return 0;
}
  • 写入特性

    lseek文件指针失效,在文件末尾附加。

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include <stdlib.h>

int main(){
    char str_write1[] = "999";
    char str_write2[] = "000";
    char rbuf1[100];
    char rbuf2[100];
    int fd;
    int size;    
       int currpos;

    //在当前目录创建或者打开文件
    fd = open("testing.txt",O_RDWR|O_APPEND|O_CREAT, 0644);
        if(fd<0){
        printf("open and creat failed");
    }

    //移动文件指针到开头
    currpos = lseek(fd, 0 , SEEK_SET);
    if(currpos!=0){
    printf("lseek error");
    return -1;
    }

    //打印当前文件指针位置
    printf("file pointer position: %d\n",currpos);

    //向文件中写入
    size = write(fd, "999", strlen("999"));
    close(fd);
    return 0;
}

动文件指针到开头
currpos = lseek(fd, 0 , SEEK_SET);
if(currpos!=0){
printf(“lseek error”);
return -1;
}

//打印当前文件指针位置
printf("file pointer position: %d\n",currpos);

//向文件中写入
size = write(fd, "999", strlen("999"));
close(fd);
return 0;

}

# #============= hal_nfc_default ============== # allow hal_nfc_default hal_secure_element_default:binder call; # 允许init执行此文件 allow init hal_nfc_default_exec:file execute; # 允许init进程切换到hal_nfc_default域 allow init hal_nfc_default:process transition; # 设置自动类型转换:当init执行该文件时,进程域变为hal_nfc_default type_transition init hal_nfc_default_exec:process hal_nfc_default; allow hal_nfc_default aidl_nfc_service:service_manager { find add }; get_prop(hal_nfc, vendor_nfc_prop) set_prop(hal_nfc, vendor_nfc_prop) allow hal_nfc_default self:process { fork signal }; allow hal_nfc_default vendor_toolbox_exec:file rx_file_perms; allow hal_nfc_default servicemanager:service_manager find; allow hal_nfc_default aidl_nfc_service:service_manager add; allow hal_nfc_default devlog:sock_file write; #allow hal_nfc_default nxpese_hwservice:hwservice_manager find; allow hal_nfc_default nxpnfc_hwservice:hwservice_manager { find add }; # allow hal_nfc_default vendor_hal_nxpnfc_service:service_manager { find add }; # allow hal_nfc_default nfc_device:chr_file { read write }; # allow hal_nfc_default nfc_data_file:file getattr; # allow hal_nfc_default nfc_vendor_data_file:dir { getattr add_name read write search remove_name }; # allow hal_nfc_default nfc_vendor_data_file:file { getattr open create read write unlink setattr append }; allow hal_nfc_default nfc_vendor_debug_data_file:dir { rw_dir_perms }; allow hal_nfc_default nfc_vendor_debug_data_file:file { create_file_perms }; # allow hal_nfc_default secure_element_vendor_data_file:file { getattr open read write unlink create setattr }; # allow hal_nfc_default secure_element_vendor_data_file:dir search; 有无语法错误
07-14
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> #include <time.h> #include <errno.h> #include <ctype.h> #include <signal.h> #include <arpa/inet.h> #include <sys/wait.h> #define PORT 80 #define MAX_CLIENTS 100 #define BUFFER_SIZE 8192 #define ROOT_DIR "" int debug_mode = 1; // 调试模式开关 pid_t main_pid; // 日志函数 void log_message(const char *type, const char *message, int level) { if (level > 0 && !debug_mode) return; time_t now = time(NULL); char *time_str = ctime(&now); time_str[strlen(time_str) - 1] = '\0'; char log_buffer[1024]; snprintf(log_buffer, sizeof(log_buffer), "[%s] %s: %s", time_str, type, message); printf("%s\n", log_buffer); // 写入日志文件(使用O_APPEND保证多进程写入安全) int log_fd = open("webserver.log", O_WRONLY | O_CREAT | O_APPEND, 0644); if (log_fd >= 0) { dprintf(log_fd, "%s\n", log_buffer); close(log_fd); } } // 获取绝对路径 void get_absolute_path(char *abs_path, const char *rel_path) { char cwd[1024]; if (getcwd(cwd, sizeof(cwd))) { snprintf(abs_path, 1024, "%s/%s/%s", cwd, ROOT_DIR, rel_path); } else { snprintf(abs_path, 1024, "%s/%s", ROOT_DIR, rel_path); } } // 路径安全检查和规范化 int normalize_path(char *path) { // 移除多余的斜杠 char *src = path; char *dst = path; while (*src) { if (*src == '/' && *(src + 1) == '/') { src++; continue; } *dst++ = *src++; } *dst = '\0'; // 检查路径遍历攻击 if (strstr(path, "..") != NULL) { return 0; } return 1; } // 获取文件类型 const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); if (ext) { 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, ".json") == 0) return "application/json"; if (strcmp(ext, ".jpg") == 0 || 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, ".ico") == 0) return "image/x-icon"; if (strcmp(ext, ".svg") == 0) return "image/svg+xml"; if (strcmp(ext, ".txt") == 0) return "text/plain"; if (strcmp(ext, ".pdf") == 0) return "application/pdf"; } return "application/octet-stream"; } // 发送HTTP响应 void send_response(int client_fd, int status, const char *status_text, const char *content_type, const char *body, size_t body_len) { char header[512]; int header_len = snprintf(header, sizeof(header), "HTTP/1.1 %d %s\r\n" "Content-Type: %s\r\n" "Content-Length: %zu\r\n" "Connection: close\r\n\r\n", status, status_text, content_type, body_len); write(client_fd, header, header_len); if (body && body_len > 0) { write(client_fd, body, body_len); } } // 发送文件 void send_file(int client_fd, const char *path) { char abs_path[1024]; get_absolute_path(abs_path, path); if (!normalize_path(abs_path)) { const char *forbidden = "403 Forbidden"; send_response(client_fd, 403, "Forbidden", "text/plain", forbidden, strlen(forbidden)); log_message("SECURITY", "Path traversal attempt detected", 0); return; } char debug_msg[1024]; snprintf(debug_msg, sizeof(debug_msg), "Accessing file: %s", abs_path); log_message("DEBUG", debug_msg, 1); FILE *file = fopen(abs_path, "rb"); if (!file) { char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "Failed to open %s: %s", abs_path, strerror(errno)); log_message("ERROR", error_msg, 0); const char *not_found = "404 Not Found"; send_response(client_fd, 404, "Not Found", "text/plain", not_found, strlen(not_found)); return; } fseek(file, 0, SEEK_END); long file_size = ftell(file); fseek(file, 0, SEEK_SET); if (file_size <= 0) { fclose(file); const char *server_error = "500 Internal Server Error (Empty file)"; send_response(client_fd, 500, "Internal Server Error", "text/plain", server_error, strlen(server_error)); log_message("ERROR", "File is empty", 0); return; } char *buffer = malloc(file_size); if (!buffer) { fclose(file); const char *server_error = "500 Internal Server Error"; send_response(client_fd, 500, "Internal Server Error", "text/plain", server_error, strlen(server_error)); log_message("ERROR", "Memory allocation failed", 0); return; } size_t read_size = fread(buffer, 1, file_size, file); fclose(file); if (read_size != (size_t) file_size) { free(buffer); const char *server_error = "500 Internal Server Error"; send_response(client_fd, 500, "Internal Server Error", "text/plain", server_error, strlen(server_error)); log_message("ERROR", "File read error", 0); return; } const char *content_type = get_content_type(path); send_response(client_fd, 200, "OK", content_type, buffer, file_size); free(buffer); } // 处理目录请求 void handle_directory(int client_fd, const char *path) { char index_path[1024]; snprintf(index_path, sizeof(index_path), "%s/Contact.html", path); struct stat st; if (stat(index_path, &st) == 0 && S_ISREG(st.st_mode)) { send_file(client_fd, index_path); } else { const char *not_found = "404 Directory index not found"; send_response(client_fd, 404, "Not Found", "text/plain", not_found, strlen(not_found)); log_message("ERROR", "Directory index not found", 0); } } // 处理客户端请求(现在在子进程中执行) void handle_client(int client_fd) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer) - 1); if (bytes_read <= 0) { close(client_fd); return; } buffer[bytes_read] = '\0'; if (debug_mode) { char req_msg[BUFFER_SIZE + 50]; snprintf(req_msg, sizeof(req_msg), "Request:\n%.*s", (int) bytes_read, buffer); log_message("REQUEST", req_msg, 1); } char method[16], path[256], protocol[16]; if (sscanf(buffer, "%15s %255s %15s", method, path, protocol) != 3) { const char *bad_request = "400 Bad Request"; send_response(client_fd, 400, "Bad Request", "text/plain", bad_request, strlen(bad_request)); close(client_fd); return; } // 完全忽略 favicon.ico 请求 if (strcmp(path, "/favicon.ico") == 0) { close(client_fd); return; // 直接返回,不发送任何响应 } // 处理路径 char full_path[1024]; if (strcmp(path, "/") == 0) { // 默认显示 Contact.html strcpy(full_path, "Contact.html"); } else { // 跳过开头的斜杠 strcpy(full_path, path + 1); } // 获取绝对路径用于文件检查 char abs_path[1024]; get_absolute_path(abs_path, full_path); // 调试信息 char debug_msg[1024]; snprintf(debug_msg, sizeof(debug_msg), "Resolved path: %s", abs_path); log_message("DEBUG", debug_msg, 1); // 处理GET请求 if (strcmp(method, "GET") == 0) { struct stat st; if (stat(abs_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { // 如果是目录,尝试查找目录下的 Contact.html handle_directory(client_fd, abs_path); } else { // 发送文件 send_file(client_fd, full_path); } } else { // 文件不存在 char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "File not found: %s", abs_path); log_message("ERROR", error_msg, 0); const char *not_found = "404 Not Found"; send_response(client_fd, 404, "Not Found", "text/plain", not_found, strlen(not_found)); } } // 处理POST请求 else if (strcmp(method, "POST") == 0) { // 查找请求体 char *body = strstr(buffer, "\r\n\r\n"); if (body) { body += 4; // 跳过空行 // 记录POST数据 char log_msg[1024]; snprintf(log_msg, sizeof(log_msg), "POST data: %.*s", (int) (bytes_read - (body - buffer)), body); log_message("INFO", log_msg, 1); // 简单的POST处理示例 const char *response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 12\r\n" "Connection: close\r\n\r\n" "POST Received"; write(client_fd, response, strlen(response)); } else { const char *bad_request = "400 Bad Request (Missing body)"; send_response(client_fd, 400, "Bad Request", "text/plain", bad_request, strlen(bad_request)); } } // 处理其他HTTP方法 else { const char *not_allowed = "405 Method Not Allowed"; send_response(client_fd, 405, "Method Not Allowed", "text/plain", not_allowed, strlen(not_allowed)); } close(client_fd); } // 僵尸进程回收函数 void reap_zombies(int sig) { while (waitpid(-1, NULL, WNOHANG) > 0); } // 清理函数 void cleanup() { log_message("INFO", "Server shutting down", 0); } void handle_signal(int sig) { if (getpid() != main_pid) { _exit(0); // 子进程直接退出 } else if (sig == SIGINT || sig == SIGTERM) { log_message("INFO", "Received termination signal, shutting down", 0); exit(0); // 主进程正常退出 } } int main() { main_pid = getpid(); // 第一行获取主进程PID // 仅主进程注册清理函数 if (getpid() == main_pid) { atexit(cleanup); } // 设置信号处理 signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); signal(SIGCHLD, reap_zombies); // 添加子进程回收信号 // 创建套接字 int server_fd; 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))) { perror("setsockopt failed"); exit(EXIT_FAILURE); } // 绑定地址 struct sockaddr_in address; 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, MAX_CLIENTS) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } // 检查根目录是否存在 char root_path[1024]; get_absolute_path(root_path, ""); struct stat st; if (stat(root_path, &st) != 0 || !S_ISDIR(st.st_mode)) { char error_msg[256]; snprintf(error_msg, sizeof(error_msg), "Root directory '%s' not found or inaccessible", root_path); log_message("FATAL", error_msg, 0); exit(EXIT_FAILURE); } else { char info_msg[256]; snprintf(info_msg, sizeof(info_msg), "Serving files from: %s", root_path); log_message("INFO", info_msg, 0); } log_message("INFO", "Server started successfully (Multi-process)", 0); printf("Multi-process server running on port %d\n", PORT); // 主循环 while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &addr_len); if (client_fd < 0) { if (errno == EINTR) continue; // 信号中断处理 perror("accept failed"); continue; } // 记录客户端信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_addr . sin_addr, client_ip, INET_ADDRSTRLEN); char conn_msg[256]; snprintf(conn_msg, sizeof(conn_msg), "Client connected: %s:%d", client_ip, ntohs(client_addr . sin_port)); log_message("INFO", conn_msg, 1); // 创建子进程处理请求 pid_t pid = fork(); if (pid == 0) { // 子进程 close(server_fd); // 关闭监听套接字 handle_client(client_fd); _exit(EXIT_SUCCESS); // 处理完成后退出 } else if (pid > 0) { // 父进程 close(client_fd); // 关闭父进程中不需要的客户端套接字 } else { perror("fork failed"); close(client_fd); } } close(server_fd); return 0; } #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <sys/epoll.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> #include <time.h> #include <arpa/inet.h> #define PORT 80 #define MAX_EVENTS 1024 #define BUFFER_SIZE 8192 #define ROOT_DIR "" int debug_mode = 1; // 连接状态结构体 typedef struct { int fd; char read_buf[BUFFER_SIZE]; size_t read_len; char write_buf[BUFFER_SIZE]; size_t write_len; size_t write_sent; int keep_alive; struct sockaddr_in client_addr; } connection_t; // 日志函数 void log_message(const char *type, const char *message, int level) { if (level > 0 && !debug_mode) return; time_t now = time(NULL); char *time_str = ctime(&now); time_str[strlen(time_str) - 1] = '\0'; char log_buffer[1024]; snprintf(log_buffer, sizeof(log_buffer), "[%s] %s: %s", time_str, type, message); printf("%s\n", log_buffer); // 写入日志文件(使用O_APPEND保证多进程写入安全) int log_fd = open("webserver.log", O_WRONLY | O_CREAT | O_APPEND, 0644); if (log_fd >= 0) { dprintf(log_fd, "%s\n", log_buffer); close(log_fd); } } // 获取绝对路径 void get_absolute_path(char *abs_path, const char *rel_path) { char cwd[1024]; if (getcwd(cwd, sizeof(cwd))) { snprintf(abs_path, 1024, "%s/%s/%s", cwd, ROOT_DIR, rel_path); } else { snprintf(abs_path, 1024, "%s/%s", ROOT_DIR, rel_path); } } // 路径安全检查和规范化 int normalize_path(char *path) { // 移除多余的斜杠 char *src = path; char *dst = path; while (*src) { if (*src == '/' && *(src + 1) == '/') { src++; continue; } *dst++ = *src++; } *dst = '\0'; // 检查路径遍历攻击 if (strstr(path, "..") != NULL) { return 0; } return 1; } // 获取文件类型 const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); if (ext) { 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, ".json") == 0) return "application/json"; if (strcmp(ext, ".jpg") == 0 || 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, ".ico") == 0) return "image/x-icon"; if (strcmp(ext, ".svg") == 0) return "image/svg+xml"; if (strcmp(ext, ".txt") == 0) return "text/plain"; if (strcmp(ext, ".pdf") == 0) return "application/pdf"; } return "application/octet-stream"; } // 设置非阻塞IO void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { perror("fcntl F_GETFL"); exit(EXIT_FAILURE); } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("fcntl F_SETFL"); exit(EXIT_FAILURE); } } // 创建HTTP响应头 void build_http_header(char *header, int status, const char *status_text, const char *content_type, size_t content_len, int keep_alive) { snprintf(header, BUFFER_SIZE, "HTTP/1.1 %d %s\r\n" "Content-Type: %s\r\n" "Content-Length: %zu\r\n" "Connection: %s\r\n" "Server: epoll-server\r\n\r\n", status, status_text, content_type, content_len, keep_alive ? "keep-alive" : "close"); } // 发送文件响应 void send_file_response(connection_t *conn, const char *path) { char abs_path[1024]; get_absolute_path(abs_path, path); if (!normalize_path(abs_path)) { const char *forbidden = "403 Forbidden"; build_http_header(conn -> write_buf, 403, "Forbidden", "text/plain", strlen(forbidden), conn -> keep_alive); memcpy(conn -> write_buf + strlen(conn -> write_buf), forbidden, strlen(forbidden)); conn -> write_len = strlen(conn -> write_buf); return; } int fd = open(abs_path, O_RDONLY); if (fd == -1) { const char *not_found = "404 Not Found"; build_http_header(conn -> write_buf, 404, "Not Found", "text/plain", strlen(not_found), conn -> keep_alive); memcpy(conn -> write_buf + strlen(conn -> write_buf), not_found, strlen(not_found)); conn -> write_len = strlen(conn -> write_buf); return; } struct stat st; fstat(fd, &st); off_t file_size = st . st_size; const char *content_type = get_content_type(path); build_http_header(conn -> write_buf, 200, "OK", content_type, file_size, conn -> keep_alive); size_t header_len = strlen(conn -> write_buf); // 使用sendfile零拷贝发送文件 conn -> write_len = header_len; conn -> write_sent = 0; // 如果文件很小,直接读取到内存 if (file_size < BUFFER_SIZE - header_len) { ssize_t n = read(fd, conn -> write_buf + header_len, file_size); if (n > 0) { conn -> write_len += n; } close(fd); } else { // 大文件使用sendfile在发送时处理 close(fd); // 实际发送在事件循环中处理 } } // 处理HTTP请求 void handle_request(connection_t *conn) { // 解析请求行 char method[16], path[256], protocol[16]; if (sscanf(conn -> read_buf, "%15s %255s %15s", method, path, protocol) != 3) { const char *bad_request = "400 Bad Request"; build_http_header(conn -> write_buf, 400, "Bad Request", "text/plain", strlen(bad_request), 0); memcpy(conn -> write_buf + strlen(conn -> write_buf), bad_request, strlen(bad_request)); conn -> write_len = strlen(conn -> write_buf); return; } // 检查Keep-Alive conn -> keep_alive = (strstr(conn -> read_buf, "Connection: keep-alive") != NULL); // 忽略favicon if (strcmp(path, "/favicon.ico") == 0) { conn -> write_len = 0; return; } // 处理路径 char full_path[1024]; if (strcmp(path, "/") == 0) { strcpy(full_path, "Contact.html"); } else { strcpy(full_path, path + 1); } // 处理GET请求 if (strcmp(method, "GET") == 0) { send_file_response(conn, full_path); } // 处理POST请求 else if (strcmp(method, "POST") == 0) { const char *response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 12\r\n" "Connection: close\r\n\r\n" "POST Received"; strncpy(conn -> write_buf, response, strlen(response)); conn -> write_len = strlen(response); conn -> keep_alive = 0; // POST后关闭连接 } // 其他方法 else { const char *not_allowed = "405 Method Not Allowed"; build_http_header(conn -> write_buf, 405, "Method Not Allowed", "text/plain", strlen(not_allowed), 0); memcpy(conn -> write_buf + strlen(conn -> write_buf), not_allowed, strlen(not_allowed)); conn -> write_len = strlen(conn -> write_buf); } } // 初始化服务器套接字 int init_server_socket() { int server_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (server_fd == -1) { perror("socket"); exit(EXIT_FAILURE); } int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in address = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(PORT) }; if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) { perror("bind"); close(server_fd); exit(EXIT_FAILURE); } if (listen(server_fd, SOMAXCONN) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } return server_fd; } int main() { int server_fd = init_server_socket(); log_message("INFO", "IO Multiplexing server started (epoll)", 0); // 创建epoll实例 int epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1"); close(server_fd); exit(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) == -1) { perror("epoll_ctl: server_fd"); close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); } // 事件循环 struct epoll_event events[MAX_EVENTS]; connection_t *connections[MAX_EVENTS] = {0}; while (1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); break; } 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 = accept(server_fd, (struct sockaddr *) &client_addr, &addr_len); if (client_fd == -1) { perror("accept"); continue; } set_nonblocking(client_fd); // 创建连接对象 connection_t *conn = malloc(sizeof(connection_t)); if (!conn) { close(client_fd); continue; } *conn = (connection_t){ .fd = client_fd, .read_len = 0, .write_len = 0, .write_sent = 0, .keep_alive = 0, .client_addr = client_addr }; // 添加到epoll监控 ev . events = EPOLLIN | EPOLLET; // 边缘触发模式 ev . data . ptr = conn; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) { perror("epoll_ctl: client_fd"); free(conn); close(client_fd); continue; } connections[client_fd] = conn; char ip_str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_addr . sin_addr, ip_str, INET_ADDRSTRLEN); char log_msg[128]; snprintf(log_msg, sizeof(log_msg), "Client connected: %s:%d", ip_str, ntohs(client_addr . sin_port)); log_message("INFO", log_msg, 1); } // 客户端事件 else { connection_t *conn = events[i] . data . ptr; // 可读事件 if (events[i] . events & EPOLLIN) { ssize_t count = read(conn -> fd, conn -> read_buf + conn -> read_len, BUFFER_SIZE - conn -> read_len - 1); if (count == -1) { // 错误或EAGAIN if (errno != EAGAIN && errno != EWOULDBLOCK) { goto close_conn; } } else if (count == 0) { // 客户端关闭连接 goto close_conn; } else { conn -> read_len += count; conn -> read_buf[conn -> read_len] = '\0'; // 检查是否收到完整请求 if (strstr(conn -> read_buf, "\r\n\r\n")) { if (debug_mode) { log_message("REQUEST", conn -> read_buf, 1); } handle_request(conn); // 修改为监听写事件 ev . events = EPOLLOUT | EPOLLET; ev . data . ptr = conn; epoll_ctl(epoll_fd, EPOLL_CTL_MOD, conn -> fd, &ev); } } } // 可写事件 if (events[i] . events & EPOLLOUT) { if (conn -> write_len > 0) { ssize_t count = write(conn -> fd, conn -> write_buf + conn -> write_sent, conn -> write_len - conn -> write_sent); if (count == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { goto close_conn; } } else { conn -> write_sent += count; // 全部发送完成 if (conn -> write_sent >= conn -> write_len) { if (conn -> keep_alive) { // 保持连接,重置状态 conn -> read_len = 0; conn -> write_len = 0; conn -> write_sent = 0; // 修改为监听读事件 ev . events = EPOLLIN | EPOLLET; ev . data . ptr = conn; epoll_ctl(epoll_fd, EPOLL_CTL_MOD, conn -> fd, &ev); } else { goto close_conn; } } } } } // 错误事件 if (events[i] . events & (EPOLLERR | EPOLLHUP)) { goto close_conn; } continue; close_conn: // 关闭连接并清理 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn -> fd, NULL); close(conn -> fd); free(conn); connections[conn -> fd] = NULL; } } } close(server_fd); close(epoll_fd); return 0; } 前一个代码是可正常运行的,后一个不行,请你基于此信息再分析原因
最新发布
08-09
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值