实验项目名称:linux下文件进程间通讯实验

大二上某科技大学某海滨老师布置的最烦人的linux实验,任务要求使用C语言编程进行程序交互,(本来C语言教的课时就短,连指针都没有教),就让用C写这么高难度的代码(对我们来说),对想拿高分的同学太不友好了。在临近期末的时候,用了一个周,硬生生地学了C和进程间通讯,硬给它肝出来了,呜呜呜。现在大三,当时学的啥一点都不记得了,只有痛苦的回忆。

代码供学弟学妹和感兴趣的人参考。前人栽树后人乘凉了属于是。

一、实验目的:

  1. 掌握Linux环境下进程的创建函数;
  2. 掌握Linux环境下进程间通讯技术。

二、实验内容:

采用C语言编程,设计结构体:学生,包含姓名,性别,年龄,班级信息。设计两个程序,一个程序负责提供命令交互,另一个程序接收命令,实现对于文件的读写,对students文件进行维护和查询。两者间通讯方式自己设计。实现的功能包括:

  1. 录入:可以添加一个学生信息
  2. 查询:可以根据姓名查询并显示
  3. 删除:可以根据姓名删除一个学生
  4. 排序:可以根据姓名或班级进行排序,并重新写入文件
  5. 统计:可以统计学生人数,以及性别分别为男生或女生的人数。

三、实验环境:

Ubuntu系统

四、实验原理:

  1. FIFO (命名管道) 的使用:
    Client和Server程序通过一个命名的管道(FIFO)进行通信。FIFO相当于一个特殊的文件,允许一个进程向其中写入数据,而另一个进程从中读取数据。
    在本实验中,Client程序将用户的命令写入FIFO,而Server程序从FIFO读取这些命令并进行处理。

  2. Client程序的命令输入和发送:
    Client程序提供一个简单的命令行界面,让用户输入命令,如添加、删除、查找学生等。
    输入的命令通过sendCommand函数发送给Server。该函数使用open、write和close系统调用来操作FIFO。

  3. Server程序的命令接收和处理:
    Server程序使用open、read、close系统调用从FIFO读取命令。
    收到命令后,Server根据命令类型(如add、find、delete等)执行相应的操作。

  4. 结构体的应用:
    使用Student结构体来组织和存储学生信息,包括姓名、性别、年龄和班级。
    在添加学生信息时,Server程序会创建一个新的Student结构体实例,并将其写入文件。

  5. 文件的读写:
    Server程序使用标准的文件I/O操作(如fopen、fprintf、fscanf、fclose)来维护学生信息。
    学生信息存储在一个名为students.txt的文本文件中,每行代表一个学生的信息。

#students.txt存储结构如下
niu,Male,13,2b
wangwu,Male,23,2b
zhangsan,Female,12,3b
su,Male,21,4b

五、实验步骤:

  1. 编写Client程序:
    实现命令行界面,接收用户输入的命令。
    使用sendCommand函数将命令通过FIFO发送给Server。

  2. 编写Server程序:
    循环等待并读取从FIFO传来的命令。
    根据命令类型执行对应的操作,如添加、查找、删除学生等。

  3. 编译程序:
    使用GCC编译器分别编译Client和Server程序。

  4. 运行程序:
    先运行Server程序,然后运行Client程序。
    在Client程序中测试不同命令,观察并验证Server的响应。

  5. 调试和优化:
    根据运行结果对程序进行调试和优化。
    确保程序能正确处理各种输入,并且能稳定运行。

六、实验结果:

对于每个命令(如add、find、delete等),提供Client和Server程序的截图,展示命令的输入和相应的输出。

1、先打开两个终端,分别使用./Client./Server命令执行交互程序。

   ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/5eb147ceefe74738878d3c8e7a500346.png)

2、对于add命令:

先使用cat命令查看students.txt文件中的学生信息
在这里插入图片描述

正常的输入:

执行./Client的终端
在这里插入图片描述

执行./Server的终端,屏幕打印“Add success”
在这里插入图片描述

查看添加后的students.txt文件
在这里插入图片描述

异常输入:

当输入的命令不符合要求时,执行./Server命令的终端会给出相应的提示
若只输入一个add

执行./Client的终端
在这里插入图片描述

执行./Server命令的终端显示“Incomplete add command.”
在这里插入图片描述

3、对于find命令:

正常的输入:
执行./Client的终端
在这里插入图片描述

执行./Server的终端
在这里插入图片描述

异常输入:
当输入的命令不符合要求时,执行./Server命令的终端会给出相应的提示,若只输入一个find
执行./Client的终端
在这里插入图片描述

执行./Server的终端,显示“No name provided for find command.”
在这里插入图片描述

4、对于delete命令

正常的输入:

先使用cat命令查看students.txt文件的数据:
在这里插入图片描述

使用delete命令进行学生的删除:
执行./Client的终端
在这里插入图片描述

执行./Server的终端,屏幕打印“Student deleted”
在这里插入图片描述

查看删除后的students.txt文件
在这里插入图片描述

异常输入:
当输入的命令不符合要求时,执行./Server命令的终端会给出相应的提示, 若只输入一个delete
执行./Client的终端
在这里插入图片描述

执行./Server的终端显示“No name provided for delete command.”
在这里插入图片描述

5、对于sort命令:

查看排序前的students.txt文件
在这里插入图片描述

在执行./Client命令的终端上输入sort后,会出现‘name’与‘class’的选项,让用户选择排序的方式。此图为按照class排序
在这里插入图片描述

执行./Server的终端,屏幕打印“Sort success”
在这里插入图片描述

按照class选项排序的结果如下:
在这里插入图片描述

可以看出,按照班级进行了升序排序
按照name排序:
执行./Client的终端
在这里插入图片描述

执行./Server的终端,屏幕打印“Sort success”
在这里插入图片描述

按照name选项排序的结果如下:
在这里插入图片描述

可以看出,按照姓名的首字母进行了升序排序

6、对于count命令:

执行./Client的终端
在这里插入图片描述

执行./Server的终端,屏幕打印“Total students:4,Male:3,Female:1”
在这里插入图片描述

7、对于exit命令:

执行./Client的终端
在这里插入图片描述

执行./Server的终端,屏幕打印“Goodbye!”

在这里插入图片描述

8、若命令输入错误:

执行./Client的终端
在这里插入图片描述

执行./Server命令的终端显示“Unknown command: abc”

在这里插入图片描述

七、实验总结与分析:

实验收获:

  1. 进程间通信理解:深入了解了Linux下的进程间通信机制,特别是命名管道(FIFO)。理解了如何创建和使用FIFO以及其在进程间通信中的作用。

  2. C语言文件操作应用:掌握了使用C语言进行文件读写的方法,包括打开文件、读取文件、写入文件以及关闭文件的操作。这在管理学生信息的存储和检索方面非常关键。

  3. 结构化数据管理:通过设计Student结构体,实践了如何使用结构体来组织和管理复杂数据。这增强了对C语言数据结构的理解。

遇到的问题及解决办法:

1、FIFO的同步问题:初期在Client和Server之间通信时遇到了数据同步的问题。通过在Server端正确地打开和关闭FIFO,以及在Client端发送完命令后进行适当的等待,解决了这一问题。

2、内存管理:在处理动态分配的内存时遇到了挑战,特别是在排序功能中。通过使用malloc和realloc以及确保及时释放内存,解决了内存泄露的问题。

3、字符串处理的错误:最初在解析从FIFO读取的字符串时遇到了一些问题,如段错误。这是因为错误地处理了strtok的返回值。通过添加对strtok返回值的检查,确保了程序的稳定性。

实验心得:
本实验不仅加深了对Linux系统编程的理解,特别是在进程间通信方面,还提高了对C语言的掌握程度,特别是在文件操作和数据结构管理方面。

八、附录:

Server.c的关键代码:

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

#define FIFO_FILE "myfifo"
#define STUDENT_FILE "students.txt"
// 学生结构定义
typedef struct {
    char name[50];
    char gender[10];
    int age;
    char class[50];
} Student;
// 函数声明
void addStudent(Student student);
void findStudent(char* name);
void deleteStudent(char* name);
void sortStudents();
void countStudents(int* male, int* female);

int main() {
    char buffer[1024];
    int fd;
   // 检查FIFO是否已经存在
    if (access(FIFO_FILE, F_OK) == -1) {
        if (mkfifo(FIFO_FILE, 0666) == -1) {
            perror("Error creating FIFO");
            return 1;
        }
    }
    

    while (1) {
	// 从FIFO读取命令
        fd = open(FIFO_FILE, O_RDONLY);
        if (fd == -1) {
            perror("Error opening FIFO");
            continue;
         }
        ssize_t numRead = read(fd, buffer, sizeof(buffer));
        close(fd);
          if (numRead <= 0) {
            perror("Error reading from FIFO");
            continue;
        }

        buffer[numRead] = '\0'; // 确保字符串正确终止
        char* command = strtok(buffer, "|");
	 if (command == NULL) {
            fprintf(stderr, "Invalid command received.\n");
            continue;
         }
	// 命令处理逻辑
        if (strcmp(command, "exit") == 0) {
            printf("Goodbye!\n");
            break;
        } else if (strcmp(command, "add") == 0) {// ... [添加学生的逻辑]
            Student s;
             char *name = strtok(NULL, "|");
             char *gender = strtok(NULL, "|");
    		char *ageStr = strtok(NULL, "|");
    		char *class = strtok(NULL, "|");

    	  if (name == NULL || gender == NULL || ageStr == NULL || class == NULL) {
        	fprintf(stderr, "Incomplete add command.\n");
        	continue;
    	   }
    		strcpy(s.name, name);
    		strcpy(s.gender, gender);
    		s.age = atoi(ageStr);
    		strcpy(s.class, class);
    		addStudent(s);
        } else if (strcmp(command, "find") == 0) {//查找学生
            char* name = strtok(NULL, "|");
            findStudent(name);
        } else if (strcmp(command, "delete") == 0) { //删除学生
            char* name = strtok(NULL, "|");
            deleteStudent(name);
        } else if (strcmp(command, "sort") == 0) {//排序学生
            char* sortBy = strtok(NULL, "|");
           if (sortBy != NULL) {
                sortStudents(sortBy);
                
            } else {
                fprintf(stderr, "No sort criteria specified.\n");
                }
        } else if (strcmp(command, "count") == 0) {//统计学生
            int male = 0, female = 0;
            countStudents(&male, &female);
            printf("Total students: %d, Male: %d, Female: %d\n", male + female, male, female);
        }else {
            fprintf(stderr, "Unknown command: %s\n", command);
          }
    }
	// 清除FIFO文件
    unlink(FIFO_FILE);
    return 0;
}


void addStudent(Student student) {
    FILE* file = fopen(STUDENT_FILE, "a");
    if (file == NULL) {
        perror("Error opening file");
        return;
    }
    fprintf(file, "%s,%s,%d,%s\n", student.name, student.gender, student.age, student.class);
     printf("Add success!\n");
    fclose(file);
}
void findStudent(char* name) {
    if (name == NULL) {
        fprintf(stderr, "No name provided for find command.\n");
        return;
    }

    FILE* file = fopen(STUDENT_FILE, "r");
    char line[1024];
    int found = 0;

    if (file == NULL) {
        perror("Error opening file");
        return;
    }

    while (fgets(line, sizeof(line), file)) {
        char tempLine[1024];  // 创建临时字符串用于复制line,因为strtok会修改原字符串
        strcpy(tempLine, line);

        char* token = strtok(tempLine, ",");
        if (token != NULL && strcmp(token, name) == 0) {
            found = 1;
            break; // 找到匹配的学生,停止循环
        }
    }

    fclose(file);

    if (!found) {
        printf("Student not found.\n");
    } else {
        // 打印找到的学生的全部信息
        char* token;
        int field = 0;
        token = strtok(line, ",");
        while (token != NULL) {
            switch (field) {
                case 0: printf("Name: %s\n", token); break;
                case 1: printf("Gender: %s\n", token); break;
                case 2: printf("Age: %s\n", token); break;
                case 3: printf("Class: %s\n", token); break;
            }
            token = strtok(NULL, ",");
            field++;
        }
    }
}

void deleteStudent(char* name) {
    if (name == NULL) {
        fprintf(stderr, "No name provided for delete command.\n");
        return;
    }

    FILE* file = fopen(STUDENT_FILE, "r");
    FILE* tempFile = fopen("temp.txt", "w");
    char line[1024];
    int found = 0;

    if (file == NULL || tempFile == NULL) {
        perror("Error opening file");
        if (file != NULL) fclose(file);
        if (tempFile != NULL) fclose(tempFile);
        return;
    }

    while (fgets(line, sizeof(line), file)) {
        char tempLine[1024];  // 为了保护原始line字符串,使用临时字符串
        strcpy(tempLine, line);

        char* token = strtok(tempLine, ",");
        // 仅当当前行的姓名与要删除的姓名不匹配时,才将其写入临时文件
        if (token == NULL || strcmp(token, name) != 0) {
            fputs(line, tempFile);
        } else {
            found = 1;
        }
    }

    fclose(file);
    fclose(tempFile);

    if (found) {
        remove(STUDENT_FILE);
        rename("temp.txt", STUDENT_FILE);
        printf("Student deleted\n");
    } else {
        printf("Student not found\n");
        remove("temp.txt");  // 删除未使用的临时文件
    }
}

// 比较函数,用于qsort
int compareStudentsByName(const void* s1, const void* s2) {
    const Student *student1 = (const Student*)s1;
    const Student *student2 = (const Student*)s2;
    return strcmp(student1->name, student2->name);
}

int compareStudentsByClass(const void* s1, const void* s2) {
    const Student *student1 = (const Student*)s1;
    const Student *student2 = (const Student*)s2;
    return strcmp(student1->class, student2->class);
}


// 重新实现的sortStudents函数
void sortStudents(const char* sortBy) {
    FILE *file = fopen(STUDENT_FILE, "r");
    Student *students = NULL;
    int size = 0, capacity = 10;

    if (file == NULL) {
        perror("Error opening file");
        return;
    }

    // 动态分配内存
    students = malloc(capacity * sizeof(Student));
    if (students == NULL) {
        perror("Memory allocation failed");
        fclose(file);
        return;
    }

    // 读取文件内容到动态数组
    while (!feof(file)) {
        if (size == capacity) {
            capacity *= 2;
            students = realloc(students, capacity * sizeof(Student));
            if (students == NULL) {
                perror("Memory reallocation failed");
                break;
            }
        }
        fscanf(file, "%[^,],%[^,],%d,%[^\n]\n", students[size].name, students[size].gender, &students[size].age, students[size].class);
        size++;
    }

    fclose(file);

    // 使用qsort排序
    if (strcmp(sortBy, "name") == 0) {
        qsort(students, size, sizeof(Student), compareStudentsByName);
    } else if (strcmp(sortBy, "class") == 0) {
        qsort(students, size, sizeof(Student), compareStudentsByClass);
    }

    // 将排序后的数据写回文件
    file = fopen(STUDENT_FILE, "w");
    for (int i = 0; i < size; i++) {
        fprintf(file, "%s,%s,%d,%s\n", students[i].name, students[i].gender, students[i].age, students[i].class);
    }
     printf("Sort success\n");
    fclose(file);
    free(students);
}


void countStudents(int* male, int* female) {
    FILE* file = fopen(STUDENT_FILE, "r");
    char line[1024];
    *male = 0;
    *female = 0;

    if (file == NULL) {
        perror("Error opening file");
        return;
    }

    while (fgets(line, sizeof(line), file)) {
        char* gender = strtok(line, ",");
        gender = strtok(NULL, ","); // 第二个字段是性别

        if (gender != NULL && strcmp(gender, "Male") == 0) {
            (*male)++;
        } else if (gender != NULL && strcmp(gender, "Female") == 0) {
            (*female)++;
        }
    }

    fclose(file);
}




Client.c程序关键代码

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

#define FIFO_FILE "myfifo"

// 向FIFO发送命令的函数
void sendCommand(const char* command) {
    int fd = open(FIFO_FILE, O_WRONLY);
    if (fd == -1) {
        perror("Error opening FIFO");
        return;
    }
    if (write(fd, command, strlen(command) + 1) == -1) {
        perror("Error writing to FIFO");
    }
    close(fd);
}
// 打印用户可用命令的菜单
void printMenu() {
    printf("============================================================\n");
    printf("Available commands:\n");
    printf("  add    - Add a new student     (add|name|gender|age|class)\n");
    printf("  find   - Find a student by name                (find|name)\n");
    printf("  delete - Delete a student by name            (delete|name)\n");
    printf("  sort   - Sort students\n");
    printf("  count  - Count students\n");
    printf("  exit   - Exit the program\n");
    printf("============================================================\n");
    printf("Enter your command: ");
}

int main() {
    char command[1024];
    // 检查FIFO是否已经存在
    if (access(FIFO_FILE, F_OK) == -1) {
        if (mkfifo(FIFO_FILE, 0666) == -1) {
            perror("Error creating FIFO");
            return 1;
        }
    }

    while (1) {
        printMenu();
        fgets(command, sizeof(command), stdin);
        command[strcspn(command, "\n")] = 0; // 去除换行符

        if (strcmp(command, "exit") == 0) {
            sendCommand(command);
            break;// 如果输入的命令是exit,则退出程序
        } else if (strcmp(command, "sort") == 0) {
		 // 如果命令是sort,则询问用户排序依据
            char sortCommand[1024];
            printf("Sort by 'name' or 'class'? ");
            fgets(sortCommand, sizeof(sortCommand), stdin);
            sortCommand[strcspn(sortCommand, "\n")] = 0;
		// 将排序依据添加到命令中
            strcat(command, "|");
            strcat(command, sortCommand);
            sendCommand(command);
        } else {
            sendCommand(command);
        }
    }
     // 程序结束时删除FIFO文件
    unlink(FIFO_FILE);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值