为什么 while((pid = waitpid(-1, &stat, WNOHANG)) > 0)能处理所有子进程

本文详细解析了SIGCHLD信号处理程序及其与waitpid函数的关联,阐述了如何通过while循环配合waitpid处理多个子进程的退出,确保父进程能够回收所有僵尸子进程,即使在信号丢失的情况下。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在unp中有一节代码是这样的:

void sig_chld(int signo)
{
       pid_t   pid;
       int     stat;
       
       while((pid = waitpid(-1, &stat, WNOHANG)) > 0){
               printf("child %d terminated\n", pid);
       }
        return;
}

见unp第110页。

是什么?

它是一个SIGCHLD信号处理程序。当父进程的一个子进程结束时,会给父进程发送一个SIGCHLD信号。

核心在哪儿?

当父进程的5个子进程同时向父进程发送SIGCHILD时会发生什么?

父进程只会调用一次信号处理程序,并丢弃其余四个信号。虽然只接受了一个SIGCHLD信号,但是

while((pid = waitpid(-1, &stat, WNOHANG)) > 0)

这条语句会处理所有的僵尸子进程。这就意味着,即使丢失了一部分SIGCHLD信号,父进程也能够回收所有的子进程。

为什么?

事实上,waitpid与SIGCHLD信号的关联并不大。waitpid的函数构造是,回收第一个僵尸子进程。而加上while之后就能够回收所有在while运行期间内结束的子进程。与SIGCHLD关系不大。

 

现在来整理一下这道题,题目要求是: 实现一个简单的类似 Shell 的程序(下面称为简易 Shell,要求如下: 1.可以在程序中输入命令(要求编写的另一个程序)的路径并在子进程中执行 2.在调用命令时可以将参数传给命令 3.可以从命令中输入内容,发送给简单 Shell 接收,并打印出来。 4.如果简易 Shell 收到的内容为 stop,则 Shell 将命令进程关闭。 5.直接在简易 Shell 中输入 exit,简易 Shell 自己退出 我写的main.c文件如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #define MAX_INPUT 1024 /* max input legth */ #define MAX_ARGS 64 /* max number of arguments */ #define MAX_PATH 1024 /* max path legth */ int main() { char input[MAX_INPUT] = {0}; /* Input buffer */ char *args[MAX_ARGS] = {NULL}; /* Argument array */ char current_dir[MAX_PATH] = {0}; /* Current working directory */ pid_t pid = 0; int pipe_fd[2] = {0}; /* Pipe */ /* Get current working directory */ if (getcwd(current_dir, MAX_PATH) == NULL) { perror("getcwd failed"); exit(EXIT_FAILURE); } while (1) { /* Display current directory(green) */ printf("\033[1;32m%s\033[0m$ ", current_dir); fflush(stdout); /* Read input */ if (fgets(input, MAX_INPUT, stdin) == NULL) { break; } /* Remove \n */ input[strcspn(input, "\n")] = '\0'; /* Skip empty input*/ if (strlen(input) == 0) { continue; } /* Exit command */ if (strcmp(input, "exit") == 0) { printf("Exit command received. Shutting down.\n"); break; } /* Read command */ int i = 0; char *token = strtok(input, " "); while (token != NULL && i < MAX_ARGS - 1) { args[i++] = token; token = strtok(NULL, " "); } args[i] = NULL; /* cd */ if (strcmp(args[0], "cd") == 0) { char *path = (i > 1) ? args[1] : getenv("HOME"); if (chdir(path) == -1) { perror("chdir failed"); } else { /* Update current directory */ if (getcwd(current_dir, MAX_PATH) == NULL) { perror("getcwd failed"); } } continue; } /* Create pipe */ if (pipe(pipe_fd) == -1) { perror("pipe failed"); continue; } /* Create child process */ pid = fork(); if (pid < 0) { perror("fork failed"); close(pipe_fd[0]); close(pipe_fd[1]); continue; } if (pid == 0) { /* Child */ /* Close read end */ close(pipe_fd[0]); /* redirect to write end */ dup2(pipe_fd[1], STDOUT_FILENO); close(pipe_fd[1]); printf("Executing command: %s\n", args[0]); execvp(args[0], args); /* Fail */ perror("execvp failed"); exit(EXIT_FAILURE); } else { /* Father process */ /* Close write end */ close(pipe_fd[1]); char buffer[MAX_INPUT]; ssize_t bytes_read; int stop_detected = 0; /* Read output */ while ((bytes_read = read(pipe_fd[0], buffer, MAX_INPUT - 1)) > 0) { buffer[bytes_read] = '\0'; printf("%s", buffer); fflush(stdout); /* Check stop */ if (strstr(buffer, "stop") != NULL) { stop_detected = 1; break; } } /* Close rean end */ close(pipe_fd[0]); if (stop_detected) { if (kill(pid, SIGTERM) == -1 && errno != ESRCH) { perror("kill failed"); } printf("Command terminated by 'stop' signal\n"); } waitpid(pid, NULL, 0); } memset(args, 0, sizeof(args)); } return 0; } 请结合要求和代码,帮我写一份报告文档,详解说明思路/步骤、测试结果
08-07
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #define MAX_INPUT 1024 #define MAX_ARGS 64 // 全局变量记录后台进程PID pid_t background_pid = -1; void execute_command(char **args, int background) { pid_t pid = fork(); if (pid == 0) { // 子进程执行区域 printf("我是子进程;pid:%d, ppid:%d, pid=%d \n",getpid(),getppid(),pid); sleep(1); execvp(args[0], args); //在子进程中使用execvp允许在不终止父进程的情况下运行新程序。父进程可以继续执行,同时子进程执行不同的任务。 perror("execvp failed"); exit(EXIT_FAILURE); } else if (pid > 0) { // 父进程执行区域 if (!background) { waitpid(pid, NULL, 0); // 等待前台进程结束 } else { background_pid = pid; // 记录后台进程PID printf("[%d] running in background\n", pid); } printf("我是父进程;pid:%d, ppid:%d, pid=%d \n",getpid(),getppid(),pid); sleep(1); } else { perror("fork failed"); } } int main() { char input[MAX_INPUT]; char *args[MAX_ARGS]; char *token; int background; while (1) { printf("mysh> "); fflush(stdout); // 读取用户输入 if (!fgets(input, MAX_INPUT, stdin)) break; // 处理换行符 input[strcspn(input, "\n")] = '\0'; // 解析输入 int i = 0; token = strtok(input, " "); while (token != NULL && i < MAX_ARGS - 1) { args[i++] = token; token = strtok(NULL, " "); } args[i] = NULL; // 空命令处理 if (i == 0) continue; // 检查后台运行标识 (&) background = 0; if (i > 0 && strcmp(args[i-1], "&") == 0) { background = 1; args[i-1] = NULL; } if (strcmp(args[0], "exit") == 0) { break; } else if (strcmp(args[0], "stop") == 0) { if (background_pid != -1) { kill(background_pid, SIGTERM); printf("Terminated process [%d]\n", background_pid); background_pid = -1; } else { printf("No background process running\n"); } } else { // 执行外部命令 execute_command(args, background); } } return EXIT_SUCCESS; } 上面是一个简易shell,main函数中的stop 模块的作用是什么?怎么验证该功能/
最新发布
08-08
网络流媒体播放器(网络由局域网实现) 核心功能是要求实现多进程访问(即存在多个终端用户可以访问,并下载视频资源),网络访问,要求存在守护进程 下载:二进制拷贝vedio目录下的文件。 提示输入printf语句要使用中文 文件要封装, 我使用的系统是redhat6.5,gcc --version结果是:gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-4) =======Welcome kun_Player======= 1.注册 2.用户登录 3.忘记密码 用户menu 1:下载视频 2:上传文件 3:用户修改密码/输入 4:注销账号 下载视频menu 1.列出可下载视频文件 2.选择下载视频/创建一个"id+movie"目录,将下载的视频放在该目录下 这是我的文件[root@Redhat6 player]# ll -a 总用量 28 drwxr-xr-x 3 root root 4096 7月 28 12:34 . drwxr-xr-x 12 root root 4096 7月 25 10:59 .. -rw-r--r-- 1 root root 0 7月 25 11:03 client.c -rw-r--r-- 1 root root 0 7月 25 11:02 Makefile -rw-r--r-- 1 root root 1907 7月 25 21:04 player.h -rw-r--r-- 1 root root 0 7月 25 11:03 service.c -rw-r--r-- 1 root root 2762 7月 25 21:03 user.c -rw-r--r-- 1 root root 0 7月 25 11:01 user.db drwxr-xr-x 2 root root 4096 7月 28 12:34 video‘ pwd (video) = /c_code/player7/video ============================================================= 这是我当前的player.h文件 #ifndef __KUN_PLAYER_H__ #define __KUN_PLAYER_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <share.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <signal.h> #include <sys/wait.h> #include <pthread.h> #include <syslog.h> #include <errno.h> #include <time.h> #include <arpa/inet.h> #define SERVER_PORT 8888 #define MAX_CLIENTS 10 #define BUFFER_SIZE 1024 #define VIDEO_DIR "/c_code/player7/video/" #define USER_DB "/c_code/player7/user.db" extern char current_user_id[20]; typedef struct user { char id[20]; char pwd[20]; } User; typedef struct node { void *data; struct node *next; struct node *prev; } Node; typedef struct file_info { char file_name[120]; unsigned long file_size; }File_info; // 菜单功能 void main_menu(); void regis_menu(); void login_menu(); void forget_pwd_menu(); void user_menu(const char *user_id); void download_menu(int sockfd); void upload_menu(int sockfd); void change_password_menu(const char *user_id); void delete_account_menu(const char *user_id); // 用户管理 Node *create_userlist_head(); void add_user(Node **head, User *user); void delete_user(Node **head, const char *id); Node *find_user_byid(Node *head, const char *id); void save_user_file(Node *head, const char *filename); void load_user_file(Node **head, const char *filename); void free_user_list(Node *head); // 网络功能 int init_server(); void handle_client(int client_fd); void download_file(int sockfd, const char *filename); void upload_file(int client_fd, const char *filename); void list_videos(int sockfd); int connect_server(); // 守护进程 void daemonize(); #endif 该函数ERRP在share.h中 //容错宏 20 //第一个参数:表示条件 21 //第二个参数:表示错误信息 22 //第三个参数:表示处理结果 退出 23 #define ERRP(con, info, ret) do{ \ 24 if (con) \ 25 { \ 26 printf(#info" false!(errline : %d fun c : %s file : %s)\n", __LINE__, __func__, __FILE__); \ 27 ret; \ 28 } \ 29 }while(0) ========================================================================= 这是我目前的user.c #include "player.h" Node* create_userlist_head() { Node* head = (Node*)malloc(sizeof(Node)); ERRP(head == NULL, "内存申请失败", goto ERR1); head->data = NULL; head->prev = head; head->next = head; return head; ERR1: printf("内存申请失败!\n"); return NULL; } void add_user(Node** head, User* user) { ERRP(*head == NULL || user == NULL, "head或用户数据异常", goto ERR2); Node* new_node = (Node*)malloc(sizeof(Node)); ERRP(new_node == NULL, "内存申请失败", goto ERR1); new_node->data = user; new_node->next = (*head)->next; new_node->prev = *head; (*head)->next->prev = new_node; (*head)->next = new_node; return; ERR1: printf("内存申请失败!\n"); return; ERR2: printf("head或其他用户数据异常\n"); return; } void delete_user(Node** head, const char* id) { ERRP(*head == NULL || id == NULL, "head或id异常", goto ERR2); Node* todelete = find_user_byid(*head, id); if (todelete == NULL) { printf("id错误,没查询到该用户\n"); return; } todelete->prev->next = todelete->next; todelete->next->prev = todelete->prev; free(todelete->data); // 释放User数据 free(todelete); return; ERR2: printf("head或其他用户数据异常\n"); return; } Node* find_user_byid(Node* head, const char* id) { ERRP(head == NULL || id == NULL, "head或id异常", goto ERR2); Node* current = head->next; while (current != head) { User* user = (User*)current->data; if (strcmp(user->id, id) == 0) { return current; } current = current->next; } return NULL; ERR2: printf("head或其他用户数据异常\n"); return NULL; } void save_user_file(Node* head, const char* filename) { ERRP(head == NULL || filename == NULL, "head或文件名异常", goto ERR2); int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); ERRP(fd == -1, open , goto ERR3); Node* current = head->next; while (current != head) { User* user = (User*)current->data; ssize_t written = write(fd, user, sizeof(User)); ERRP(written != sizeof(User), "写入失败", goto ERR4); current = current->next; } close(fd); return; ERR2: printf("head或其他用户数据异常\n"); return; ERR3: printf("文件打开失败\n"); return; ERR4: printf("写入文件失败\n"); close(fd); return; } void load_user_file(Node** head, const char* filename) { ERRP(head == NULL || filename == NULL, "head或文件名异常", goto ERR2); int fd = open(filename, O_RDONLY); if (fd == -1) { printf("用户数据库不存在。\n"); return; } User temp; while (read(fd, &temp, sizeof(User)) == sizeof(User)) { User* new_user = (User*)malloc(sizeof(User)); if (new_user == NULL) { printf("内存申请失败!\n"); close(fd); return; } *new_user = temp; add_user(head, new_user); } close(fd); return; ERR2: printf("head或其他用户数据异常\n"); return; } void free_user_list(Node* head) { ERRP(head == NULL , "head", goto ERR2); Node* current = head->next; while (current != head) { Node* next = current->next; free(current->data); free(current); current = next; } free(head); return; ERR2: printf("head异常\n"); return; } ========================================================== 这是我目前的service.c #include "player.h" static Node *user_list = NULL; static int server_fd = -1; /* SIGCHLD信号处理函数 用于回收子进程资源,防止僵尸进程 */ void sigchld_handler(int sig) { while (waitpid(-1, NULL, WNOHANG) > 0); } /* * 守护进程化函数 * 将当前进程转变为守护进程 */ void daemonize() { pid_t pid = fork(); ERRP(pid < 0, "创建守护进程失败", exit(EXIT_FAILURE)); // 父进程退出 if (pid > 0) { exit(EXIT_SUCCESS); } // 创建新会话 if (setsid() < 0) { exit(EXIT_FAILURE); } // 忽略信号 signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); pid = fork(); ERRP(pid < 0, "第二次fork失败", exit(EXIT_FAILURE)); if (pid > 0) { exit(EXIT_SUCCESS); } umask(0); chdir("/"); int x=0; //当进程从普通进程转变为守护进程时,它会继承父进程所有打开的文件描述符 //这些文件描述符可能包括终端设备、管道、临时文件等,守护进程不再需要这些资源 //如果不关闭,会导致系统资源(文件描述符)被无意义占用 for (x= sysconf(_SC_OPEN_MAX); x >= 0; x--) { close(x); } } /* * 初始化服务器 * 创建socket,绑定端口并开始监听 * 返回: 成功返回0,失败返回-1 */ int init_server() { struct sockaddr_in server_addr; // 创建TCP socket server_fd = socket(AF_INET, SOCK_STREAM, 0); ERRP(server_fd < 0, "创建socket失败", return -1); // 设置socket选项,允许地址重用 int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // 配置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(SERVER_PORT); // 绑定socket到地址 ERRP(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0, "绑定端口失败", close(server_fd); return -1); // 开始监听,最大连接数为MAX_CLIENTS ERRP(listen(server_fd, MAX_CLIENTS) < 0, "监听失败", close(server_fd); return -1); printf("服务器启动成功,监听端口: %d\n", SERVER_PORT); return 0; } /* * 列出视频目录中的文件 * sockfd: 客户端socket文件描述符 */ void list_videos(int sockfd) { DIR *dir; struct dirent *entry; char buffer[BUFFER_SIZE] = {0}; // 打开视频目录 dir = opendir(VIDEO_DIR); if (dir == NULL) { strcpy(buffer, "无法打开视频目录"); send(sockfd, buffer, strlen(buffer), 0); return; } // 构建文件列表字符串 strcpy(buffer, "可下载视频列表:\n"); while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } strcat(buffer, entry->d_name); strcat(buffer, "\n"); } closedir(dir); send(sockfd, buffer, strlen(buffer), 0); } /* * 发送文件给客户端 * sockfd: 客户端socket文件描述符 * filename: 要发送的文件名 */ void send_file(int sockfd, const char *filename) { } void up_load(int client_fd, const char *filename) { } /* * 处理客户端请求 * client_fd: 客户端socket文件描述符 */ // 在 handle_client 函数中添加新的命令处理 void handle_client(int client_fd) { char buffer[BUFFER_SIZE]; ssize_t bytes_received; while ((bytes_received = recv(client_fd, buffer, BUFFER_SIZE, 0)) > 0) { buffer[bytes_received] = '\0'; if (strncmp(buffer, "LIST", 4) == 0) { list_videos(client_fd); } else if (strncmp(buffer, "DOWNLOAD ", 9) == 0) { send_file(client_fd, buffer + 9); } else if (strncmp(buffer, "UPLOAD ", 7) == 0) { up_load(client_fd,buffer + 7); } } close(client_fd); } /* * 服务器主循环 * 接受客户端连接并创建子进程处理 */ void run_server() { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int client_fd; // 设置SIGCHLD信号处理 signal(SIGCHLD, sigchld_handler); while (1) { // 接受客户端连接 client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { perror("接受连接失败"); continue; } printf("新客户端连接: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // 创建子进程处理客户端 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失败"); close(client_fd); } } } /* * 主函数 */ int main() { daemonize(); // 初始化用户数据库 user_list = create_userlist_head(); load_user_file(&user_list, USER_DB); // 初始化服务器 if (init_server() < 0) { return EXIT_FAILURE; } // 运行服务器主循环 run_server(); // 清理资源 free_user_list(user_list); close(server_fd); return EXIT_SUCCESS; } =========================================================== 这是我目前的client.c #include "player.h" char current_user_id[20] = {0}; /* * 连接到服务器 * 返回: 成功返回socket文件描述符,失败返回-1 */ int connect_server() { int sockfd; struct sockaddr_in server_addr; sockfd = socket(AF_INET, SOCK_STREAM, 0); ERRP(sockfd < 0, "创建socket失败", return -1); memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("连接服务器失败"); close(sockfd); return -1; } return sockfd; } void receive_file(int sockfd, const char *save_path) { } // 上传文件功能 void upload_menu(int sockfd) { } void download_menu(int sockfd) { struct file_info { char file_name[120]; unsigned long file_size; } file_info; char buffer[BUFFER_SIZE]; char filename[256]; printf("\n=======下载视频=======\n"); printf("1. 列出可下载视频\n"); printf("2. 输入文件名下载\n"); printf("请选择: "); int choice; scanf("%d", &choice); getchar(); if (choice == 1) { send(sockfd, "LIST", 4, 0); ssize_t bytes = recv(sockfd, buffer, sizeof(buffer) - 1, 0); if (bytes > 0) { buffer[bytes] = '\0'; printf("%s\n", buffer); } return; } printf("请输入要下载的文件名: "); fgets(filename, sizeof(filename), stdin); filename[strcspn(filename, "\n")] = '\0'; // 1. 发送下载请求 snprintf(buffer, sizeof(buffer), "DOWNLOAD %s", filename); send(sockfd, buffer, strlen(buffer), 0); // 2. 接收文件信息 ssize_t bytes = recv(sockfd, &file_info, sizeof(file_info), MSG_WAITALL); if (bytes <= 0) { printf("文件不存在或接收失败\n"); return; } // 3. 创建用户目录 char user_dir[256]; snprintf(user_dir, sizeof(user_dir), "%smovie", current_user_id); mkdir(user_dir, 0755); // 4. 准备接收文件 char save_path[PATH_MAX]; snprintf(save_path, sizeof(save_path), "%s/%s", user_dir, file_info.file_name); printf("开始下载: %s (%.2f MB)\n", file_info.file_name, file_info.file_size / (1024.0 * 1024)); int fd = open(save_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror("创建文件失败"); return; } // 5. 接收文件内容 // 6. 验证完整性 } /* * 用户菜单 * user_id: 当前登录的用户ID */ void user_menu(const char *user_id) { int sockfd = connect_server(); if (sockfd < 0) { printf("无法连接到服务器\n"); return; } int choice; while (1) { printf("\n=======用户菜单=======\n"); printf("用户: %s\n", user_id); printf("1. 下载视频\n"); printf("2. 上传文件\n"); printf("3. 修改密码\n"); printf("4. 注销账号\n"); printf("5. 退出登录\n"); printf("请选择: "); scanf("%d", &choice); getchar(); // 清除换行符 switch (choice) { case 1: download_menu(sockfd); break; case 2: upload_menu(sockfd); break; case 3: change_password_menu(user_id); break; case 4: delete_account_menu(user_id); close(sockfd); return; // 注销后返回主菜单 case 5: close(sockfd); return; default: printf("无效选择\n"); } } } /* * 用户注册菜单 */ void regis_menu() { User new_user; Node *user_list = create_userlist_head(); load_user_file(&user_list, USER_DB); printf("\n=======用户注册=======\n"); printf("请输入用户名: "); fgets(new_user.id, sizeof(new_user.id), stdin); new_user.id[strcspn(new_user.id, "\n")] = '\0'; printf("请输入密码: "); fgets(new_user.pwd, sizeof(new_user.pwd), stdin); new_user.pwd[strcspn(new_user.pwd, "\n")] = '\0'; if (find_user_byid(user_list, new_user.id) != NULL) { printf("用户名已存在\n"); free_user_list(user_list); return; } User *user = (User *)malloc(sizeof(User)); strcpy(user->id, new_user.id); strcpy(user->pwd, new_user.pwd); add_user(&user_list, user); save_user_file(user_list, USER_DB); printf("注册成功\n"); free_user_list(user_list); } /* * 用户登录菜单 */ void login_menu() { char id[20], pwd[20]; Node *user_list = create_userlist_head(); load_user_file(&user_list, USER_DB); printf("\n=======用户登录=======\n"); printf("请输入用户名: "); fgets(id, sizeof(id), stdin); id[strcspn(id, "\n")] = '\0'; printf("请输入密码: "); fgets(pwd, sizeof(pwd), stdin); pwd[strcspn(pwd, "\n")] = '\0'; Node *node = find_user_byid(user_list, id); if (node == NULL) { printf("用户名不存在\n"); } else { User *user = (User *)node->data; if (strcmp(user->pwd, pwd) == 0) { printf("登录成功\n"); strcpy(current_user_id, user->id); user_menu(user->id); } else { printf("密码错误\n"); } } free_user_list(user_list); } // 添加修改密码功能 void change_password_menu(const char *user_id) { char old_pwd[20], new_pwd[20], confirm_pwd[20]; Node *user_list = create_userlist_head(); load_user_file(&user_list, USER_DB); printf("\n=======修改密码=======\n"); printf("用户: %s\n", user_id); Node *node = find_user_byid(user_list, user_id); if (node == NULL) { printf("用户不存在\n"); free_user_list(user_list); return; } printf("请输入旧密码: "); fgets(old_pwd, sizeof(old_pwd), stdin); old_pwd[strcspn(old_pwd, "\n")] = '\0'; User *user = (User *)node->data; if (strcmp(user->pwd, old_pwd) != 0) { printf("旧密码错误\n"); free_user_list(user_list); return; } printf("请输入新密码: "); fgets(new_pwd, sizeof(new_pwd), stdin); new_pwd[strcspn(new_pwd, "\n")] = '\0'; printf("请确认新密码: "); fgets(confirm_pwd, sizeof(confirm_pwd), stdin); confirm_pwd[strcspn(confirm_pwd, "\n")] = '\0'; if (strcmp(new_pwd, confirm_pwd) != 0) { printf("两次输入密码不一致\n"); free_user_list(user_list); return; } strcpy(user->pwd, new_pwd); save_user_file(user_list, USER_DB); printf("密码修改成功\n"); free_user_list(user_list); } // 添加注销账号功能 void delete_account_menu(const char *user_id) { char confirm[20]; Node *user_list = create_userlist_head(); load_user_file(&user_list, USER_DB); printf("\n=======注销账号=======\n"); printf("警告: 此操作将永久删除您的账号!\n"); printf("用户: %s\n", user_id); printf("确认注销账号?(输入YES确认): "); fgets(confirm, sizeof(confirm), stdin); confirm[strcspn(confirm, "\n")] = '\0'; if (strcmp(confirm, "YES") == 0) { delete_user(&user_list, user_id); save_user_file(user_list, USER_DB); printf("账号已注销\n"); free_user_list(user_list); return; // 返回到主菜单 } else { printf("取消注销操作\n"); } free_user_list(user_list); } /* * 忘记密码菜单 */ void forget_pwd_menu() { printf("\n=======忘记密码=======\n"); printf("请联系管理员重置密码\n"); } /* * 主菜单 */ void main_menu() { int choice; while (1) { printf("\n=======Welcome kun_Player=======\n"); printf("1. 注册\n"); printf("2. 用户登录\n"); printf("3. 忘记密码\n"); printf("4. 退出\n"); printf("请选择: "); scanf("%d", &choice); getchar(); // 清除换行符 switch (choice) { case 1: regis_menu(); break; case 2: login_menu(); break; case 3: forget_pwd_menu(); break; case 4: return; default: printf("无效选择\n"); } } } int main() { main_menu(); return 0; } ===================flie_service.c=============================== /* 文件传输 服务器接收客户端传输文件2.0 */ #include<stdio.h> #include<unistd.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<stdlib.h> #include<string.h> #define BACKLOG 3 #define PORT 7777 #define IP "127.0.0.1" int file_access(int fd); int file_imformation_accept(int fd,char buff[],unsigned long int *file_size); //默认接收文件路径 static char filepath[50] = "/c_code/player_test/video/"; struct file_imformation{ char file_name[120]; unsigned long int file_length; }; int main(int argc,char* argv[]){ int fd; struct sockaddr_in daddr; char buff[1024]; int err; //建立套接字 fd = socket(AF_INET,SOCK_STREAM,0); if(fd < 0){ printf("套接字建立失败!\n"); } //初始化服务器地址 memset(&daddr,0,sizeof(struct sockaddr)); daddr.sin_family = AF_INET; daddr.sin_port = htons(PORT); daddr.sin_addr.s_addr = inet_addr(IP); //套接字绑定ip err = bind(fd,(struct sockaddr*)&daddr,sizeof(struct sockaddr)); if(err < 0){ printf("绑定失败\n"); } //监听端口 err = listen(fd,BACKLOG); if(err < 0){ printf("监听错误!\n"); } //接受连接 socklen_t len = sizeof(struct sockaddr); int acfd = accept(fd,(struct sockaddr*)&daddr,&len); file_access(acfd); close(acfd); close(fd); return 0; } //接收文件内容 int file_access(int fd){ unsigned long int size = -1; unsigned long int recv_size = 0; unsigned long int file_size; char buffer[1024]; char file_url[200] = {0}; //创建并打开文件 memset(file_url,0,sizeof(file_url)); memset(buffer,0,sizeof(buffer)); //获取客户端发送信息 file_imformation_accept(fd,file_url,&file_size); //创建接收文件 int filefd = open(file_url,O_RDWR|O_CREAT,0777); if(filefd == -1){ perror("文件打开失败"); return -1; } //从套接字中读取数据并写入文件 while(size){ size = read(fd,buffer,sizeof(buffer)); if(size == -1){ printf("文件传输错误\n"); remove(file_url); close(filefd); return -1; }else if(size > 0){ recv_size += size; write(filefd,buffer,size); memset(buffer,0,sizeof(buffer)); }else{ if(recv_size != file_size){ printf("错误:文件传输失败,文件大小不一致!\n"); remove(file_url); }else{ printf("接收文件成功!\n"); } close(filefd); return 0; } } } //在接受文件内容前,接受文件信息 int file_imformation_accept(int fd,char buff[],unsigned long int *file_size){ int i = 0; struct file_imformation file_im; while(i < strlen(filepath)){ buff[i] = filepath[i]; i++; } int len = read(fd,&file_im,sizeof(struct file_imformation)); if(len <= 0){ printf("错误:接收文件信息为空"); } printf("接收文件名称为%s\n",file_im.file_name); printf("接收文件大小为%ld\n",file_im.file_length); //获取文件大小 *file_size = file_im.file_length; //获取文件名 if(NULL == strcpy(&buff[i],file_im.file_name)){ printf("错误:获取文件名失败\n"); } printf("文件url是%s\n",buff); return 0; } ==================================file_client.c=================================== /* 文件传输 客户端向服务器传送文件2.0 */ #include<stdio.h> #include<unistd.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<stdlib.h> #include<string.h> #include<sys/stat.h> #define PORT 7777 #define IP "127.0.0.1" struct file_imformation{ char file_name[120]; unsigned long int file_length; }; int file_transfer(int fd,char* path,unsigned long int file_size); int file_imformation_transfer(int server_fd); void show(int count); int main(int argc,char* argv[]){ int fd; struct sockaddr_in daddr; int err; //建立套接字 fd = socket(AF_INET,SOCK_STREAM,0); if(fd < 0){ printf("socket create fail!\n"); } //初始化服务器地址 memset(&daddr,0,sizeof(struct sockaddr)); daddr.sin_family = AF_INET; daddr.sin_port = htons(PORT); daddr.sin_addr.s_addr = inet_addr(IP); //建立连接 err = connect(fd,(struct sockaddr*)&daddr,sizeof(struct sockaddr)); if(err < 0){ printf("connect fail!\n"); } file_imformation_transfer(fd); close(fd); return 0; } //传输文件内容 int file_transfer(int fd,char* path,unsigned long int file_size){ int size = 0; unsigned long sum = 0; int n = 0; char buffer[1024]; memset(buffer,0,sizeof(buffer)); int filefd = open(path,O_RDWR); if(filefd == -1){ perror("file open fail"); } while(1){ size = read(filefd,buffer,sizeof(buffer)); if(size == -1){ printf("文件传输错误\n"); close(filefd); return -1; }else if(size > 0){ size = write(fd,buffer,size); sum += size; unsigned long int p = (sum * 100)/file_size; printf("\r文件已传输%ld\%%",p); }else{ //当读取为0时,表示文件读取完毕 printf("传输成功 %ld byte!\n",sum); close(filefd); return 0; } } } //在传输文件内容之前,先传送文件信息,比如文件名 int file_imformation_transfer(int server_fd){ struct file_imformation file_im; struct stat sta; char path[200]; memset(path,0,sizeof(path)); memset(&file_im,0,sizeof(struct file_imformation)); memset(&sta,0,sizeof(struct stat)); printf("请输入文件路径和文件名:\n"); scanf("%s",path); /* 获取文件名 根据文件路径path确定文件名 加入输入path为"/home/my/text.txt"那么i定位到最后一个反斜杠,然后将文件名复制到file_im.file_name中 */ int i = strlen(path) - 1; while(path[i] != '/' && i >= 0){ i--; } i++; for(int j = 0;i < strlen(path)+1;j++){ file_im.file_name[j] = path[i]; i++; } printf("文件名为:%s\n",file_im.file_name); /* 文件大小的获取 */ if(stat(path,&sta) == -1){ perror("获取文件信息错误"); return -1; } file_im.file_length = sta.st_size; printf("文件大小为%ld\n",file_im.file_length); //向服务器发送文件名 write(server_fd,&file_im,sizeof(struct file_imformation)); //开始文件传输 file_transfer(server_fd,path,sta.st_size); return 0; } void show(int count){ while(count >0 ){ write(1,"#",1); count--; } } =========================================================================== [root@Redhat6 player_test]# ./ser 接收文件名称为upload.mp4 接收文件大小为2892683 文件url是/c_code/player_test/video/upload.mp4 接收文件成功! [root@Redhat6 player_test]# cd video/ [root@Redhat6 video]# ls upload.mp4 [root@Redhat6 video]# ll upload.mp4 -rwxr-xr-x 1 root root 2892683 7月 29 10:02 upload.mp4 [root@Redhat6 video]# ll /tools/upload.mp4 -rwxr-xr-x 1 root root 2892683 7月 28 19:31 /tools/upload.mp4 [root@Redhat6 player_test]# ./cli 请输入文件路径和文件名: /tools/upload.mp4 文件名为:upload.mp4 文件大小为2892683 文件已传输100%传输成功 2892683 byte! 此函数能实现文件的完整传输,请参考file_service.c和file_client.c仿照其处理逻辑完善 该播放器的文件上传,下载模块
07-30
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h> // for execlp #include <mqueue.h> // for mq #include "settings.h" #include "messages.h" char client2dealer_name[30]; int groupnumber = 16; pid_t processID; mqd_t Req_queue_X; MQ_REQUEST_MESSAGE req; int main (int argc, char * argv[]) { struct mq_attr attr; attr.mq_maxmsg = MQ_MAX_MESSAGES; attr.mq_msgsize = sizeof (MQ_REQUEST_MESSAGE); sprintf (client2dealer_name, "/Req_queue_%d", groupnumber); mq_unlink(client2dealer_name); Req_queue_X = mq_open (client2dealer_name, O_RDONLY | O_CREAT | O_EXCL, 0600, &attr); if(Req_queue_X == -1) { perror("Channel creation failed!"); } getattr(Req_queue_X); processID = fork(); if (processID < 0) { perror("fork() failed"); exit (1); } else if (processID == 0) { // child-stuff execlp("./client", "client", client2dealer_name, NULL); perror("execlp failed"); exit (0); } while (1) { int status; pid_t result = waitpid(processID, &status, WNOHANG); // Non-blocking check if (result > 0) { printf("Client terminated\n"); break; } ssize_t size = mq_receive(Req_queue_X, (char*)&req, sizeof(MQ_REQUEST_MESSAGE), 0); if (size == -1) { perror("Receiving failed \n"); } else if ( size == sizeof(MQ_REQUEST_MESSAGE)) { if (req.serv_type == 1 || req.serv_type == 2) { printf("Received message with id %d, type %d and value %d\n", req.req_id, req.serv_type, req.input); } else if (req.serv_type == 0) { printf("...\n"); } } else { perror("mq_receive failed partially \n"); } sleep(1); printf("I'm still alive \n"); } printf("loop ends \n"); waitpid(processID, NULL, 0); mq_close(Req_queue_X); mq_unlink(client2dealer_name); } 帮我分析下问题在哪里
03-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值