基于C语言的小型文件系统设计与实现

全套资料下载地址:https://download.csdn.net/download/sheziqiong/85593584
全套资料下载地址:https://download.csdn.net/download/sheziqiong/85593584

一、需求分析

1.1 小型文件系统介绍

科技的进步已将人类带入了信息大爆炸的时代,随着计算机科学技术的不断发展,计算机成为了许多人日常生活中必不可少的一部分,不管是个人办公娱乐还是企业员工协作,都无一不用到了计算机的信息管理、文件管理技术,其中文件系统就是计算机上有着良好功能设计的一个系统、它通过良好的文件信息组织和简便的功能操作,提供强大文件管理功能的同时,还给予了使用者莫大的便捷。

因此,此系统的应运而生可以解决了文件的信息管理问题,推动了当时计算机科学技术的深入发展。基于这种情况,本文主要描述一个小型的文件管理系统的设计与实现,需要注意的是,本系统主要是对文件描述信息的管理,并不涉及底层的磁盘存储的分配和管理。在交流和协作密切的办公环境下,一个文件系统下可能有多人协同操作,为了支持类似 Unix/Linux 的多用户协作和使用,本文件系统也实现为一个多用户模式的文件系统。

1.2 小型文件系统用户说明

本系统有两类用户:管理员 root 和 普通用户

管理员 root,可以对普通用户的添加和删除,对文件系统中所有文件/目录有增删改查的权限,其中包括对公共目录(用于管理员向各个普通用户发送文件)的所有权限。(本系统只有一个管理员账号:账号名 root,密码:root)

普通用户可以在自己的 Home 目录下对文件目录进行增删改查,除此之外还能对公共目录的文件有查询的权限。

1.3 小型文件系统功能介绍

图 1 系统功能结构图

系统主要功能包括登录,管理员和普通用户对各自的文件目录的操作功能,管理员拥有用户管理的功能,而文件目录的操作功能又包含查看和更新文件目录,以及切换目录的功能,具体说明如下:

登录:用户登录,只有登录成功的用户才能使用本系统功能

模式切换:选择菜单栏模式或命令行模式

信息的永久化处理:所有内存数据应保存到磁盘中,做永久化处理,以便数据不会丢失

文件管理:管理员可以对根目录下的所有文件目录进行管理

用户管理操作:管理员还可以对普通用户进行维护管理,添加、删除和修改用户信息;

共享目录文件的设置:一个所有用户都有权限访问的文件目录;

文件目录操作:显示当前路径信息;

文件目录操作:显示当前路径下所有文件和子目录信息;

文件目录操作:切换当前目录到上一级父目录;

文件目录操作:切换当前目录到下一级选定的子目录;

文件目录操作:在当前目录下创建一个新的空文件;

文件目录操作:在当前目录下删除一个文件;

文件目录操作:在当前目录下创建一个新文件夹;

文件目录操作:在当前目录下删除一个文件夹。

命令模式下支持的命令:useradd、userdel、passwd、id、exit、ls、cd、 mkdir、touch、rm、pwd,reboot,clear,changemode 等相关的类 Linux 命令。

1.4 小型文件系统具体功能需求分析

1.4.1 加载数据和数据永久化

功能介绍:打开系统后从磁盘加载数据,退出系统有数据要保存到磁盘,以便下次启功时可以导入到系统

用户登录

功能介绍:用户输入用户名和密码后登录,如果登录成功,进入相应的功能

菜单界面或命令行,如果失败则打印失败信息。

输入

用户名和密码

输出 命令行和菜单栏界面入口或登录失败信息和登录界面

1.4.2 系统模式切换

功能介绍:用户可以根据自己的需求自由的切换命令行模式和菜单栏模式。

输入

要切换的模式 ,或者 changemode 命令

输出

模拟命令行窗口或菜单模式等待用户操作

执行命令 useradd, userdel, passwd, id, exit, ls, cd, mkdir, touch、 rm, pwd, reboot, clear, changemode(命令行模式)

功能介绍:根据用户输入的命令执行指定操作 useradd:添加新用户。

Userdel:删除用户。

passwd:为用户设置密码。

id:查询指定用户。

exit:退出程序。

ls:查看当前目录下的文件目录。

cd:切换上下级目录,根目录。

mkdir:新建文件夹。

touch: 新建文件

rm:删除文件目录(级联删除) pwd:显示当前路径。

reboot:恢复初始化状态,文件系统内容全部清空。

clear:命令行的清屏模式。

changemode:切换模式,切换为菜单栏模式。

输入

用户命令

输出

命令执行后的效果信息。

1.4.3 用户管理-添加用户和用户(菜单栏模式)
  • 功能介绍:根据用户名添加用户家目录和用户密码。
  • 输入
  • 用户名和密码
  • 输出
  • 添加成功的信息,或者添加失败的提示。
1.4.4 用户管理-删除/修改用户(菜单栏模式)
  • 功能介绍:根据用户名,删除/修改指定用户。
  • 输入
  • 要删除/修改的用户名(选项)
  • 输出
  • 打印删除/修改成功或此用户不存在等失败的提示信息。
1.4.5 用户管理-查询用户(菜单栏模式)
  • 功能介绍:管理员查询当前系统所有用户信息或者单独某个用户的信息。
  • 输入
  • (可选:用户明)
  • 输出
  • 显示当前系统所有用户(指定用户)的信息。
1.4.6 文件目录管理-新建文件目录(菜单栏模式)
  • 功能介绍:在当前目录下新建一个文件或目录。
  • 输入
  • 文件目录名,类型(d 表示目录,-表示文件),大小(目录固定大小,以-1 代替),(所在首块磁盘块自动分配)。
  • 输出
  • 提示新建成功,或者重名冲突新建失败。
1.4.7 文件目录管理-删除/修改文件目录(菜单栏模式)
  • 功能介绍:根据文件目录名,修改文件目录的相关属性。
  • 输入
  • 文件目录名(选项)
  • 输出
  • 打印修改/删除成功或文件目录不存在的信息。
1.4.8 文件目录管理-查看文件目录信息(菜单栏模式)
  • 功能介绍:根据输入的文件目录名(编号),修改此文件目录的信息
  • 输入
  • 要查看的文件目录名(选项)
  • 输出
  • 打印文件目录的属性信息或文件目录不存在的信息
1.4.9 文件目录管理-显示当前路径信息(菜单栏模式)
  • 功能介绍:根据当前所处位置显示当前路径信息
  • 输入无
  • 输出
  • 显示当前路径信息
1.4.10 文件目录管理-显示当前文件、子目录信息(菜单栏模式)
  • 功能介绍:根据当前所处位置显示当前路径下所有文件和子目录信息
  • 输入无(2)输出
  • 显示当前路径下所有文件和子目录信息
1.4.11 文件目录管理-切换至子目录或上级目录(菜单栏模式)
  • 功能介绍:根据信息在权限内切换至任意目录
  • 输入
  • 当前目录或子目录(选项)
  • 输出
  • 切换目录成功的界面
1.4.12 文件目录管理-访问用户共享目录(菜单栏模式)
  • 功能介绍:跳转到共享目录下
  • 输入无
  • 输出
  • 切换至共享目录后显示共享目录的界面

二、系统设计

2.1 系统结构设计

2.1.1 本系统是一共分成五层:

界面层:面向用户,展示界面信息和读取用户的需求选择,初步判断信息的合法性,并把信息传递给业务处理层。

业务处理层:根据界面层传来的数据信息,判断需要进行哪种业务处理,调用并把信息传递到不同功能的函数进行实现。功能实现层:对所有的业务处理层传来的信息和所需功能进行相应的分解和函数实现,函数实现功能操作的对象的基础是数据结构层中的数据。

数据结构层:结构体,顺序表,孩子兄弟结点表示法的树等用于实现对数据的结构化保存。

持久化数据层:把数据结构层中的数据写入到文件,以及从文件中读取信息动态构建相应的表和树等数据结构。

2.1.2 数据结构设计

www.biyezuopin.cc
数据对象设计

时间对象:

typedef struct Date_struct {
    char date_str[MAX_CHAR_LEN]; //日期的整个字符串  	int year;  	int month;  	int day;
} Date;

文件目录:

typedef struct FileDir {
    char file_name[MAX_CHAR_LEN]; //文件目录名
    char type; //普通文件用- 表示,目录文件用d表示
    Date found_time; //建立时间
    int size; //大小  int first_disk; //首块磁盘号
} FD;

用户/管理员 :

typedef struct User_struct {
    char user_name[MAX_CHAR_LEN]; //用户名  char user_passwd[MAX_CHAR_LEN]; //用户密码
    char home_dir[MAX_CHAR_LEN]; //用户家目录,默认为用户名,管理员则为根目录/  Date found_time; //添加用户时间
} User;
2.1.2 数据结构设计

采用静态有序顺序表作为数据结构来存放所有的用户信息

typedef User DataType; 	//元素的数据类型
typedef struct {
    DataType list[LIST_MAXSIZE];
    int size; 	//顺序表的元素数量
} SeqList;

本系统采用孩子兄弟表示法的树作为数据结构来存放所有的文件目录及其关系,孩子兄弟表示法表示的树结构中有一个变量用于保存指向文件目录的指针,实现了文件目录的管理和组织

typedef struct Tree_srtruct {
    FD* fd; //文件目录  struct Tree_srtruct* fa; //当前目录的上一级目录  struct Tree_srtruct* child;  struct Tree_srtruct* slib;
} Tree;

三、系统详细设计与实现

3.1 界面层

用户登录页面

void LoginPage(char* account,char* password)
{   printf("                文件系统   \n");
    printf(" ========================================\n");
    printf("|                                        |\n");
    printf("|           欢迎使用文件系统!           |\n");
    printf("|               请先登录                 |\n");
    printf("|                                        |\n");
    printf(" ========================================\n");
    printf("账号:");
    scanf("%s", account);
    printf("密码:");
    scanf("%s", password);
}

处理流程:用户输入用户名和密码,并通过 name 和 passwd 的指针将结果返回

给调用函数。调用函数判断是否时管理员用户名和密码,如果是就登录成功继续进行登录后的业务,如果不是,重复等待输入。

3.1.1 模块选择界面
void ModeChangePage(int* choice)
{   printf("                文件系统   \n");
    printf(" ========================================\n");
    printf("|                                        |\n");
    printf("|               模式选择                 |\n");
    printf("|             1.菜单栏模式               |\n");
    printf("|             2.命令行模式               |\n");
    printf(" ========================================\n");
    scanf("%d", choice);
}

处理流程:等待用户输入进行模式选择,成功则根据选则进行相应功能跳转,失

败重复等待输入。 3.1.3 菜单栏模式界面

void ManagerPage(int* choice)
{   printf("                文件系统   \n");
    printf(" ========================================\n");
    printf("|             1.文件管理                 |\n");
    printf("|             2.用户管理                 |\n");
    printf("|             0.切换模式                 |\n");
    printf("|            -1.退出                     |\n");
    printf("|            -2.恢复初始化状态           |\n");
    printf(" ========================================\n");
    scanf("%d", choice);
}

www.biyezuopin.cc
处理流程:等待管理员输入进行模块选择,成功则根据选则进行相应功能跳转,失败重复等待输入或返回或退出。

3.1.2 文件管理和用户管理界面
void FDManagePage(int* choice, Tree* now_dir, char* all_fd[])
{   printf("                文件系统   \n");
    printf(" ========================================\n");
    printf("|                文件管理                |\n");
    printf("|   1.添加文件目录  2.删除文件目录       |\n");
    printf("|   3.修改文件目录  4.查看文件目录       |\n");
    printf("|   5.返回上级目录  6.进入下级目录       |\n");
    printf("|   7.显示当前路径  8.访问共享目录       |\n");
    printf("|   0.返回         -1.退出               |\n");
    printf(" ========================================\n");
    TreeTrave(now_dir, all_fd); //打印当前目录下所有文件  	scanf("%d", choice);
}

处理流程:等待管理员/用户输入进行功能选择,成功则根据选则进行相应功能跳转,失败重复等待输入或返回或退出。

void UserManagePage(int* choice)
{   printf("                文件系统   \n");
    printf(" ========================================\n");
    printf("|               用户管理                 |\n");
    printf("|      1.添加用户      2.删除用户        |\n");
    printf("|      3.修改用户      4.查询用户        |\n");
    printf("|      5.查看所有用户                    |\n");
    printf("|      0.返回         -1.退出            |\n");
    printf(" ========================================\n");
    scanf("%d", choice);
}

处理流程:等待管理员输入进行功能选择,成功则根据选则进行相应功能跳转,失败重复等待输入或返回或退出。

3.2 业务处理层

用户登录业务

void Login(SeqList* usr_list,char* login_name)

返回类型:无返回值;是否含参数:是,含有一个保存用户信息的链表指针和用户名的字符串指针。步骤:

调用 LoginPage 函数展示登录界面,并从登录界面中得到用户输入的用户名和密码;

根据用户名和密码以及存有用户信息的 usr_list 判断用户名和密码是否合法; C.合法则登录成功并跳转到模式选择页面,否则提示用户密码错误,重新展示登录界面,等待用户操作;

3.2.1 模式切换业务
int ModeChange()

返回类型:int;

是否含参数:否

步骤:

展示模式切换界面,获取选项;

根据选项进行模式选择跳转;

3.2.2 菜单模式业务

int MenuMode(SeqList* usr_list, char* success_name,Tree* root) 返回类型:int;用于切换模式

是否含参数:是,含有一个保存用户信息的链表指针和用户名的字符串指针,以及存放所有文件的结构体指针。

步骤:

判断登录者是管理员还是用户;

是管理员则展示文件管理或者用户管理模块,并响应对应模块的的请求,执行业务逻辑处理;

否则仅展示用户对应的文件管理模块进行业务处理;

返回值用于判断是否切换模式;

(本系统测试,以管理员为例)

3.2.3 命令行模式业务

int CmdMode(SeqList* usr_list, char* success_name, Tree* root) 返回类型:int;用于切换模式。

是否含参数:是,含有一个保存用户信息的链表指针和用户名的字符串指针,以及存放所有文件的结构体指针。

步骤:

输入命令行标识符;

等待用户输入命令;

根据输入的命令去调用命令执行函数;
www.biyezuopin.cc
命令执行函数把执行结果展示出来;

3.2.4 用户管理的业务处理(菜单模式)

void AddUserBP(SeqList* usr_list,Tree * home_dir) 返回类型:无。

是否含参数:是,含有一个执行当前目录的结构体指针,一个用户列表指针。

步骤:

调用 AddUserPage 获取用户的输入的新用户的名字和密码;

调用 AddUser 把得到的用户添加到用户列表 usr_list 中;

添加失败则终止业务;

添加成功则在/home 目录下创建一个与用户名相同的子目录;

void DelUserBp(SeqList* usr_list,Tree * home_dir) 返回类型:无。

是否含参数:是,含有一个执行当前目录的结构体指针,一个用户列表指针。

步骤:

调用 AddUserPage 获取用户要删除的用户名;

调用 DelUser 把该用户添加从列表 usr_list 中删除;

删除失败则中止业务;

删除成功则把/home 目录下与用户名相同的子目录删除;

void ModifyUserBP(SeqList* usr_list) 返回类型:无。

是否含参数:是,含有一个存放用户信息的列表指针。

步骤:

调用 ModifyUserPage 获取用户要修改的用户名,以及新密码;

判断密码是否为-1,-1 表示取消修改;

否则调用 ModifyUser 修改 usr_list 中的用户密码;

void IDUserBP(SeqList* usr_list) 返回类型:无。

是否含参数:是,含有一个存放用户信息的列表指针。

步骤:

调用 IDUserPage 获取用户要查看的用户名;

否则调用 IDUser 查看 usr_list 中的用户信息;

void AllUserBP(SeqList* usr_list) 返回类型:无。

是否含参数:是,含有一个存放用户信息的列表指针。

步骤:

调用 AllIDUser 要展示 usr_list 中所有的用户信息;

3.2.5 文件目录的业务处理(菜单模式)

int AddFDBP(Tree* now_dir)

返回类型:int;表示处理的成功与否。

是否含参数:是,含有一个指向当前目录的结构体指针。

步骤:

判断 now_dir 是否为目录,若非目录则终止业务处理;

调用 AddFdPage 获取用户的输入的新文件目录参数;

判断当前目录下有无同名的冲突文件或目录,有则终止业务;

新建一个新的文件目录;

把该文件目录放在当前目录下;

int DelFDBP(Tree* now_dir,char* all_fd[])

返回类型:int;表示处理的成功与否。

是否含参数:是,含有一个执行当前目录的结构体指针,以及一个指针数组(用于选定删除的文件目录)。

步骤:

调用 DelFdPage 获取用户要删除的文件目录选项;

判断该文件目录名是否存在;

存在则在当前目录下删除该文件目录;

int ModifyFDBP(Tree* now_dir, char* all_fd[]) 返回类型:int;表示处理的成功与否。

是否含参数::是,含有一个指向当前目录的结构体指针,以及一个指针数组(用于选定修改的文件目录)。

调用 ModifyFdPage 获取用户要修改的文件目录选项;;

调用 AddFdPage 获取用户的输入的新文件目录参数;

判断该文件目录名是否存在,不存在则终止业务;

判断当前目录下有无同名的冲突文件或目录,有则终止业务;

新建一个新的文件目录;

把该文件目录放在当前目录下;

int ShowFDBP(char* all_fd[])

返回类型:int;表示处理的成功与否。

是否含参数:是,含有一个指针数组(用于选定展示的文件目录)。

步骤:

调用 ShowFDPage 获取用户要查看的文件目录选项;

判断该文件目录名是否存在;

存在则展示该文件目录;

不存在则终止业务。

void CdChildBP(Tree** now_dir, char* all_fd[]) 返回类型:无;

是否含参数:是,含有一个二级指针和一个指针数组(用于选定展示的文件目录)。

步骤:

调用 CdChild 获取用户要进入的下级目录选项;

从 all_fd 中取出该目录对应的指针;

把取出的指针赋值給二级指针 now_dir 指向的内容,表示目录切换成功;

void PwdBP(Tree* now_dir) 返回类型:无。

是否含参数:是,含有一个指向当前目录的结构体指针。

步骤:

把 now_dir 的目录名存放到一个二维字符数组中;

用 p=now_dir->fa 得到当前目录的上一级目录;

把上级目录的目录名存到二维字符数组中;

重复 BC 直到目录为根目录/;

把二维字符数组中的目录名倒序输入,并在它们之间加上字符/;

3.2.6 根据不同命令进行业务处理(命令行模式)

int parseline(char *buf, char **argv) 返回类型:int;代表 argv 中的个数

是否含参数:是,含有一个字符指针 buf(命令 + 参数),和一个二级指针(buf 分割后的内容)。

步骤:

用空格替换行末换行符;

删除行首空格;

利用空格作为分割符,把 buf 中的字符分割到 argv 中;

返回分割后得到的字符串个数;

int executeCmd(char* cmdline, SeqList* usr_list, char* success_name, Tree* root, Tree** now_dir)

返回类型:int;用于切换模式。

是否含参数:是,含有一个保存用户信息的链表指针和用户名的字符串指针,以及存放所有文件的结构体指针,和指向当前目录的指针,和存放命令的字符数组。步骤:

调用 parseline 把命令进行分割;

根据分割后的内容调用不同的功能实现函数进行实现;

实现成功后展示给用户;

失败则给出提示;

3.3 功能实现层

3.3.1 文件目录相关功能实现
Date MakeCurDate() 返回类型:Date;是否含参数:无步骤:
  • 调用库函数 ctime,time,localtime 等获取当前时间;
  • 新建一个 Date 结构体;
  • 把获取到的时间分割出年月日,和时间戳字符串分别保存在 Date 里;
  • 将得到的 Date 返回;
FD* MakeFD(char* f_n, char t, int s) 返回类型:FD*;
  • 是否含参数:是,含有一个指针(指向文件名),类型字符,文件大小步骤:
  • 根据文件目录名,文件目录类型,文件目录大小生成一个文件目录结构体;
  • 调用 MakeCurDate 获取当前时间的 Date 结构体,;
  • 把 Date 结构体保存进 FD 中;
  • 返回一个指向该 FD 结构体的指针;
  • void FreeFD(FD* fd)。
  • 返回类型:无;
  • 是否含参数:是,含有一个指针(指向文件目录)步骤:
  • A:根据指向该文件目录 FD 的结构体指针,调用 free()释放该指针指向的内存。
3.3.2 树结构对文件目录的组织和管理功能实现
void TreeRootInit(Tree* root) 返回类型:无;
  • 是否含参数:是,含有一个代表根目录的树结点指针步骤:
  • 新建三个文件目录分别为根目录,共享目录和用户家目录;
  • 把根目录的指针初始化;
  • 把根目录放进根目录指针中;
  • 在根目录下加入共享目录和家目录;
void TreeNodeInit(Tree* t) 返回类型:无;

是否含参数:是,含有一个树结点指针步骤:

把指针指向的内容各个变量都置为 NULL;

void TreeAdd(Tree* t,FD* fd) 返回类型:无;
  • 是否含参数:是,含有一个代表文件目录的树结点指针,和一个文件目录指针步骤:
  • 判断 t 是否为目录,fd 是否为非空,不满足条件则结束;
  • 寻找 t 的孩子结点;
  • 寻找该孩子结点的兄弟结点,若无则跳转 F;
  • 判断该兄弟结点是否还有兄弟结点;
  • 若仍有兄弟结点,重复 CD
  • 若无兄弟结点,则新建一个结点并把 fd 放进去,作为该结点的兄弟结点
Tree* TreeGet(Tree* t, char* f_n) 返回类型:树结点指针;
  • 是否含参数:是,含有一个代表文件目录的树结点指针,和一个代表文件名的指针
  • 步骤:
  • 判断 t 是否为目录,f_d 是否为非空,不满足条件则结束;
  • 寻找 t 的孩子结点,判断该结点代表的文件目录名是否与 f_d 相同;
  • 相同则跳转到步骤 H;
  • 寻找该孩子结点的兄弟结点,判断该结点代表的文件目录名是否与 f_d 相同;
  • 相同则跳转到步骤 H;
  • 判断该兄弟结点是否还有兄弟结点;
  • 若仍有兄弟结点,跳转到步骤 D,无则结束;
  • 返回指向该树结点的指针。
void TreeDel(Tree* t, char* f_n) 返回类型:无;
  • 是否含参数:是,含有一个代表文件目录的树结点指针,和一个代表文件名的指针
  • 步骤:
  • 判断 t 是否为目录,f_d 是否为非空,不满足条件则结束;
  • 寻找 t 的孩子结点,判断该结点代表的文件目录名是否与 f_d 相同;
  • 相同则跳转到步骤 I;
  • 寻找该孩子结点的兄弟结点,判断该结点代表的文件目录名是否与 f_d 相同;
  • 相同则跳转到步骤 I;
  • 判断该兄弟结点是否还有兄弟结点;
  • 若仍有兄弟结点,跳转到步骤 D,无则结束;
  • 若改结点是 t 的孩子结点,则把 t 的孩子结点更新为该结点的第一个兄弟结点,释放该节点。
  • 若不是则把该结点所在的兄弟链里删除,并释放该结点。
void TreeTrave(Tree* t,Tree* all_FD[]) 返回类型:无;
  • 是否含参数:是,含有一个代表文件目录的树结点指针,和一个树结点指针数组步骤:
  • 判断 t 是否为目录,不满足条件则结束;
  • 寻找 t 的孩子结点,把该结点代表的文件目录名输出,并编号存在 all_fd 中;
  • 寻找该结点的兄弟结点,把该结点代表的文件目录名输出,并编号存在 all_fd 中,不存在则跳转到 F;
  • 判断该兄弟结点是否还有兄弟结点;
  • 若仍有兄弟结点,重复 CD
  • 若无兄弟结点,则新建一个结点并把 fd 放进去,作为该结点的兄弟结点
void FreeTree(Tree* t) 返回类型:无;
  • 是否含参数:是,含有一个代表文件目录的树结点指针步骤:
  • 判断 t 是否为 NULL,是则结束该函数;
  • 判断 t 是否有孩子结点,有则递归释放该孩子结点;
  • 判断 t 是否有兄弟结点,有则递归释放该兄弟结点;
  • 释放 t 结点本身;

3.3.3 用户管理功能实现

User MakeUser(char* u_name, char* u_passwd) 返回类型:一个用户结构体;
  • 是否含参数:是,含有指向用户名和密码的字符数组指针步骤:
  • 新建一个 User 结构体;
  • 把 u_name 和 u_passwd 拷贝进 User;
  • 新建一个代表当前时间的 Date 结构体,并拷贝进 User;
  • 返回一个 User 结构体;
void InitUserList(SeqList* user_list) 返回类型:无;

是否含参数:是,含有一个列表结构体指针步骤:

调用 ListInit 函数对 user_list 进行初始化;

int IsUserLegal(SeqList* user_list, char* u_name, char* passwd)
  • 返回类型:int;
  • 是否含参数:是,含有一个列表结构体指针,指向用户名和密码的字符数组指针步骤:
  • user_list 取出名为 u_name 的用户结构体指针,不存在则返回 0;
  • 存在则比较该用户的密码是否与 passwd 相同;
  • 相同则返回 1,否则返回 0;
int AddUser(SeqList* user_list, char* u_name,char* u_passwd)
  • 返回类型:int;
  • 是否含参数:是,含有一个列表结构体指针,指向用户名和密码的字符数组指针步骤:
  • 判断 user_list 中是否有为 u_name 的用户,有则返回 0;
  • 无则新建一个用户名和密码为 u_name,u_passwd 的 User;
  • 把该 User 插入到 user_list;
  • 返回 1。
int DelUser(SeqList* user_list, char* u_name)
  • 返回类型:int;
  • 是否含参数:是,含有一个列表结构体指针,指向用户名的字符数组指针步骤:
  • 判断 user_list 中是否有为 u_name 的用户,无则返回 0;
  • 有则将该 User 从 user_list 中删除;
  • 返回 1。
int ModifyUser(SeqList* user_list, char* u_name, char* u_passwd, char* home_dir) 返回类型:int;
  • 是否含参数:是,含有一个列表结构体指针,指向用户名和密码的字符数组指针步骤:
  • 判断 user_list 中是否有为 u_name 的用户,无则返回 0;
  • 有则将该 User 的密码和家目录改为 u_passwd 和 home_dir;
  • 若为-1 则不更改;
  • 返回 1。
int IDUser(SeqList* user_list, char* u_name)
  • 返回类型:int;
  • 是否含参数:是,含有一个列表结构体指针,指向用户名的字符数组指针步骤:
  • 判断 user_list 中是否有为 u_name 的用户,有则打印该用户的信息;
  • 有则打印该用户的信息,并返回 1;
  • 无则返回 0。
  • void AllIDUser(SeqList* user_list) 返回类型:无 是否含参数:是,含有一个列表结构体指针;
  • 步骤:
  • 遍历 user_list 中的用户;
  • 遍历过程中把用户的信息打印出来;

3.4 持久化数据层

3.4.1 读取配置信息
int ReadConfigMsg(SeqList* usr_list) 返回类型:int;
  • 是否含参数:是,含有一个存放用户信息的链表结构体指针。
  • 步骤:
  • 判断配置文件 config.init 是否为空,空则结束;
  • 非空则读取文件内容,看第一行是否为 1;
  • 是则表示系统属于不属于初次打开,因此调用则调用 ReadUserFromFile 读取保存用户信息的文件;
  • 否,则表示初始打开系统,读取文件中的前两行,表示管理员账号和密码;
  • 把他们读入 user_list 中,并且写入保存用户信息的文件中;
  • 往 config.init 中写入一个 1 表示已经打开过了。
3.4.2 用户数据和文件目录文件读写
void WriteUserToFile(SeqList* usr_list) 返回类型:无;
  • 是否含参数:是,含有一个存放用户信息的链表结构体指针。
  • 步骤:
  • 打开用户信息文件 user.config 是否为空;
  • 遍历 usr_list;
  • 把 usr_list 中的用户信息按二进制写入文件中;
  • 关闭文件;
void ReadUserFromFile(SeqList* usr_list) 返回类型:无;
  • 是否含参数:是,含有一个存放用户信息的链表结构体指针。
  • 步骤:
  • 打开用户信息文件 user.config;
  • 若不存在文件则结束;
  • 把读到的用户信息,保存至 usr_list 中;
  • 关闭文件;
  • void WriteTreeToFile(Tree* root) 返回类型: 无;
  • 是否含参数:是,含有一个指向根目录的树结点指针;。
  • 步骤:
  • 打开文件目录信息文件 fd.config,树结点文件 treenode.config;
  • 若不存在文件则结束;
  • 新建一个队列,将 root 结点入队列;
  • 当队列非空,则出队列,并把队列中的内容分别写入文件 fd.config 和 treenode.config;
  • 若出队指针孩子结点非空,入队列;
  • 若出队指针兄弟节点非空,再把兄弟结点入队列;
  • 重复 DEF 至队列为空,关闭所有文件。
  • Tree* ReadTreeFromFile() 返回类型: 无;
  • 是否含参数:是,含有一个指向根目录的树结点指针;。
  • 步骤:
  • 打开文件目录信息文件 fd.config,树结点文件 treenode.config;
  • 读入信息到 root,若 root 为空则结束;
  • 新建一个队列,将 root 结点入队列;
  • 当队列非空,则出队列,否则关闭所有文件,函数结束;
  • 若出队指针孩子结点非空,从文件中读入一个结点作为该出队结点的孩子结点,把读取的结点指针入队列;
  • 若出队指针兄弟结点非空,从文件中读入一个结点作为该出队结点的兄弟结点,把读取的结点指针入队列;
  • 重复 DEF。
3.4.3 初始化程序信息、保存信息终止程序、恢复原始设置

void InitProcess(SeqList** usr_list,Tree** root,char** login_name) 返回类型: 无;

是否含参数:是,含有三个二级指针,分别代表用户列表,根节点,登录名。

步骤:

新建一个保存用户信息的列表,并初始化;

调用 ReadConfigMsg()读取配置信息和用户信息;

调用 ReadTreeFromFile()读取文件目录信息;

若读取后 root 指向的内容仍为空,则创建一个内存给 root,并初始化根节点;

为登录名创建所需空间;

void KillProcess(SeqList* usr_list, Tree* root, char* login_name) 返回类型: 无;

是否含参数:是,含有三个指针,分别代表用户列表,根节点,登录名。

步骤:

调用 WriteUserToFile()把用户信息写入文件;

调用 WriteTreeToFile()把根节点代表的整个文件系统写入文件;

调用 ReadTreeFromFile()读取文件目录信息;

释放 usr_list,root,login_name 的s内存空间;

void Reboot(SeqList* usr_list, Tree* root, char* login_name) 返回类型: 无;

是否含参数:是,含有三个指针,分别代表用户列表,根节点,登录名。

步骤:

打开保存初始化配置,用户信息和文件目录树结点的文件;

把初始管理员账号密码写入配置文件;

把存储用户信息和文件信息的文件清空;

关闭所有文件。

3.5 函数调用关系

四、系统运行测试结果及分析

4.1 登录测试

测试模块如表 4-1 所示。

表 4-1

测试模块名称输入输出测试结果
管理员登录root root模式选择菜单通过
管理员登录root 123提示登录失败通过
用户登录caijunqian 123456模式选择菜单通过

4.2 命令行模块测试

注意:另外还有 clear 清空屏幕命令,reboot 恢复初始化设置命令。

4.3 菜单栏模块测试

用户管理

文件管理:

注:系统恢复初始化,退出,模式切换,数据的保存和文件读写在验收时测试。

五、总结

遇到的问题:

刚拿到题目,不知道如何下手才能更好更快的进行开发;

担心向以前写的课程系统一样,调试起来特别麻烦;

头文件的包含不是很理解;

读入 char 字符时遇到奇怪问题;

孩子兄弟表示法的树结构不知道如何写入文件后读出来还能构建成树;

多个指针类型的保存以及二级指针的灵活使用问题;

解决:

利用软件分层的思想,把系统开发分解成多个层面,想清楚每个层需要解决的问题,和提供的接口。减少各个层之前的耦合性。

每个模块的函数写完后可以新建一个测试函数,在测试函数里调用这些函数进行测试。然后 main 函数调用该测试函数对该模块进行整体测试;还可以利用宏#ifdef AAA #endif 来输出调试过程的信息。在 bug 解决完后,可以取消 def AAA。

将一些共享的,很多个文件都会用到的声明放到一个头文件中,该文件可以没有对应的.c 文件,只给其它文件用于包含头文件。

scanf 读入 char 类型的字符时,必须把键盘的缓冲区清空,因为它可读入回车空格等字符;

通过利用队列进行层序遍历的方式,把孩子兄弟表示法的二叉树写入文件中,在读取的时候,逐个读取,根据读取到的结构体的孩子结点或兄弟结点的指针是否为空判断下一个读入的结构体是否赋值为前一个的孩子结点或兄弟结点;(已在附录中给出核心代码)

通过二级指针可以实现指针的修改,char* 数组可以保存不同类型的指针,使用时进行强制转换即可。

六、附录:

核心代码

把孩子兄弟表示法的树写入文件,和读出来动态构建树的相关代码:

void WriteTreeToFile(Tree* root)
{
    Tree* p ;
    FILE* fp1 = fopen("./fd.config", "w");
    FILE* fp2 = fopen("./treenode.config", "w");
    FILE* fp3 = fopen("./numOfDisk.config", "w");
    SeqQueue myQueue;
    fprintf(fp3, "%d", G_DISK_NUM);
    QueueInit(&myQueue);
    QueueAppend(&myQueue, root);  	//将根结点入队  	while (QueueNotEmpty(&myQueue))//当队列非空
    {
        QueuePop(&myQueue, &p); //出队并写入文件
//写入文件
        fwrite((void*)p->fd, sizeof(FD), 1, fp1);
        fwrite((void*)p, sizeof(Tree), 1, fp2);
//将左孩子入队,再将右孩子入队
        if (p->child != NULL)QueueAppend(&myQueue, p->child);
        if (p->slib != NULL)QueueAppend(&myQueue, p->slib);
    }
    fclose(fp1);
    fclose(fp2);
    fclose(fp3);
}
/*依次把配置文件treenode.config、fd.config读入,构建树*/
Tree* ReadTreeFromFile()
{
    FD* fd=NULL;
    Tree* tp=NULL;
    Tree* p = NULL;
    Tree* root;
    SeqQueue myQueue;
    FILE* fp1 = fopen("./fd.config", "r");
    FILE* fp2 = fopen("./treenode.config", "r");
    FILE* fp3 = fopen("./numOfDisk.config", "r");
    QueueInit(&myQueue);
//先把根节点读入
    root = (Tree* )malloc(sizeof(Tree));
    fd = (FD*)malloc(sizeof(FD));
    fread(fd, sizeof(FD), 1, fp1);
    p = fread(root, sizeof(Tree), 1, fp2);
    if (p == NULL) {
        return NULL; //配置文件为空,不需要构建树
    }
    if(fp3!=NULL)fscanf(fp3, "%d", &G_DISK_NUM);
    root->fd = fd;
//根据根节点的child,slib是否为空构建整颗树
    QueueAppend(&myQueue, root);
    while (QueueNotEmpty(&myQueue))//当队列非空
    {
        QueuePop(&myQueue, &p); //出队  	 	if (p->child != NULL) {
//读入它孩子,入队列
        tp = (Tree*)malloc(sizeof(Tree));
        fd = (FD*)malloc(sizeof(FD));
        fread(fd, sizeof(FD), 1, fp1);
        fread(tp, sizeof(Tree), 1, fp2);
        tp->fd = fd;
        p->child = tp;
        tp->fa = p; //指向父亲节点
        QueueAppend(&myQueue, tp);
    }
    if (p->slib != NULL) {
//读入它兄弟,入队列
        tp = (Tree*)malloc(sizeof(Tree));
        fd = (FD*)malloc(sizeof(FD));
        fread(fd, sizeof(FD), 1, fp1);
        fread(tp, sizeof(Tree), 1, fp2);
        tp->fd = fd;
        p->slib = tp;
        tp->fa = p->fa; //指向父亲节点
        QueueAppend(&myQueue, tp);
    }
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
return root;
}

c(sizeof(Tree));
fd = (FD*)malloc(sizeof(FD));
fread(fd, sizeof(FD), 1, fp1);
p = fread(root, sizeof(Tree), 1, fp2);
if (p == NULL) {
return NULL; //配置文件为空,不需要构建树
}
if(fp3!=NULL)fscanf(fp3, “%d”, &G_DISK_NUM);
root->fd = fd;
//根据根节点的child,slib是否为空构建整颗树
QueueAppend(&myQueue, root);
while (QueueNotEmpty(&myQueue))//当队列非空
{
QueuePop(&myQueue, &p); //出队 if (p->child != NULL) {
//读入它孩子,入队列
tp = (Tree*)malloc(sizeof(Tree));
fd = (FD*)malloc(sizeof(FD));
fread(fd, sizeof(FD), 1, fp1);
fread(tp, sizeof(Tree), 1, fp2);
tp->fd = fd;
p->child = tp;
tp->fa = p; //指向父亲节点
QueueAppend(&myQueue, tp);
}
if (p->slib != NULL) {
//读入它兄弟,入队列
tp = (Tree*)malloc(sizeof(Tree));
fd = (FD*)malloc(sizeof(FD));
fread(fd, sizeof(FD), 1, fp1);
fread(tp, sizeof(Tree), 1, fp2);
tp->fd = fd;
p->slib = tp;
tp->fa = p->fa; //指向父亲节点
QueueAppend(&myQueue, tp);
}
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
return root;
}



  • 22
    点赞
  • 161
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
基于C语言的端口扫描工具设计实现涉及到网络编程和系统编程的知识。以下是一个简单的基于C语言的端口扫描工具的设计实现过程。 首先,我们需要引入相关的头文件,包括netinet/in.h、sys/socket.h和arpa/inet.h。这些头文件提供了一些网络编程所需的函数和数据结构。 接下来,我们需要创建一个Socket,并设置为非阻塞模式。使用socket()函数创建一个套接字,并使用fcntl()函数设置为非阻塞模式。 然后,我们需要定义一个循环,用于遍历要扫描的端口范围。在循环中,使用connect()函数尝试连接每个端口,并通过检查返回值来确定端口的状态。如果返回值是0,表示端口开放,可以成功连接。如果返回值是ECONNREFUSED,表示端口关闭。如果返回值是其他错误码,表示其他情况,需要根据具体情况进行处理。 在实现过程中,为了提高扫描速度,可以使用多线程或者多进程进行并发扫描。通过创建多个线程或者进程来同时扫描不同的端口,可以减少扫描的时间。 最后,我们需要将扫描结果输出到日志文件或者控制台。可以使用printf()函数将结果打印到控制台,或者使用file I/O函数将结果写入到日志文件中。 总结起来,基于C语言的端口扫描工具设计实现需要了解网络编程和系统编程的知识。通过创建Socket、设置为非阻塞模式、使用循环遍历端口范围、使用connect()函数进行连接尝试、使用多线程或多进程进行并发扫描,并将结果输出到日志文件或控制台,可以实现一个简单的端口扫描工具。这只是一个简单示例,实际的实现过程中还需要考虑异常处理、性能优化等方面的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值