具体实施方式
参见图1、图2.本发明一种透明、通用的文件缓存系统,为一个单进程多线程的系统,它由核心模块11、配置管理模块21、消息队列管理模块31、通信模块41和缓存操作 模块51,共五个模块组成;其中:
核心模块11、配置管理模块21位于线程0中运行,通信模块41位于线程1中运行,缓存操作模块51位于线程2中运行,消息队列管理模块31为全局共享。
本发明所述的缓存系统运行于Linux操作系统,并且需要有内存文件系统Tmpfs提供文件缓存的底层支持。
参见图1~图15本发明的一个具体实施例如下:
1.核心模块11
本模块由四部分组成
1)模块初始化函数111;
2)模块清理函数112;
3)命令处理函数113;
各部分具体实现如下:
1)模块初始化函数111
模块初始化函数111用于本模块启动时完成一系列初始化工作,参见图3其具体步骤如下:
-步骤1111:调用配置管理模块21的初始化函数213启动该模块;
-步骤1112:调用配置管理模块21的配置信息读取函数215,获取消息队列最大长度;
-步骤1113:调用配置管理模块21的配置信息读取函数216,获取通信模块的监听端口号;
-步骤1114:调用配置管理模块21的配置信息读取函数217,获取需缓存根路径;
-步骤1115:调用配置管理模块21的配置信息读取函数218,获取缓存根路径;
-步骤1116:调用消息队列管理模块31的初始化函数3110启动该模块;
-步骤1117:调用通信模块51的初始化函数517启动该模块;
-步骤1118:调用缓存操作模块41的初始化函数414启动该模块;
-步骤1119:调用命令处理函数113,以接收、处理来自命令行的管理命令。
2)模块清理函数112
模块清理函数112在命令处理函数113接收退出命令后执行,它用于本模块退出时完成一系列清理工作,参见图4。其具体步骤如下:
-步骤1121:调用通信模块51的清理函数518停止该模块;
-步骤1122:调用缓存操作模块41的清理函数415停止该模块;
-步骤1123:调用消息队列管理模块31的清理函数3111停止该模块;
-步骤1124:调用配置管理模块21的清理函数214停止该模块。
3)命令处理函数113
命令处理函数113接收和处理来自命令行的管理命令。它在一个无限循环中等待来自命令行的输入,并对每个输入进行解析。如果检测到停止缓存系统命令,则结束循环,然后调用模块清理函数112。
2.配置管理模块21
该模块由以下7个部分组成:
1)配置信息结构211;
2)配置信息结构指针212;
3)模块初始化函数213;
4)模块清理函数214;
5)配置信息读取函数215、216、217、218;
6)配置载入函数219;
7)系统配置文件2110。
各部分具体实现如下:
1)配置信息结构211
配置信息结构211包含如下4个字段:
-消息队列最大长度,无符号短整型
-监听端口号,无符号短整型
-缓存根路径,字符型指针
-需缓存根路,字符型指针
其中,消息队列最大长度字段表示消息队列的最大长度;监听端口号字段表示通信模块监听的端口号;缓存根路径字段表示允许缓存的路径的根;需缓存根路径字段表示缓存文件存放的路径的根。
2)配置信息结构指针212
配置信息结构指针212为配置信息结构211类型的指针,用于存取系统配置文件 2110获取的配置信息。
3)模块初始化函数213
模块初始化函数213在模块启动时被调用,完成配置信息结构指针212的空间分配,然后调用配置载入函数219载入配置信息。
4)模块清理函数214
模块清理函数214回收为配置信息结构指针212分配的空间。
5)配置信息读取函数215、216、217、218
配置信息读取函数共4个,分别是:
-获取消息队列最大长度函数215
消息队列最大长度函数215返回消息队列最大长度,类型为无符号短整型。
-获取监听的端口号216
监听端口号216返回监听端口号,类型为无符号短整型。
-获取需缓存根路径217
需缓存根路径217返回需缓存根路径,类型为字符型指针。
-获取缓存根路径218
缓存根路径218返回缓存根路径,类型为字符型指针。
上述配置信息读取函数215、216、217、218是配置管理模块21对外提供的接口,通过这四个函数,核心模块11能够方便地获取系统的配置信息,同时预防了配置文件2110的意外修改。
6)配置载入函数219
配置载入函数219在初始化函数213中被调用,它读取并解析系统的配置文件 2110,然后将配置信息存储到由配置信息结构指针212指向的空间中,并返回给调用者。
7)系统配置文件2110
系统配置文件2110是一个位于同一目录下的名为aclm.config的文本文件。其格式参见图5。
3.消息队列管理模块31
该模块由以下11个部分组成:
1)缓存操作消息结构311
2)消息队列项结构312;
3)消息队列最大长度变量313;
4)当前“待处理”消息队列长度变量314;
5)“待处理”消息队列队头指针315;
6)“待处理”消息队列互斥访问锁316;
7)当前“已处理”消息队列长度变量317;
8)“已处理”消息队列队头指针318;
9)“已处理”消息队列互斥访问锁319;
10)模块初始化函数3110;
11)模块清理函数3111;
12)消息队列操作函数3112、3113、3114、3115、3116。
本发明中,消息队列采用链表进行实现。各部分具体实现如下:
1)缓存操作消息结构311
缓存操作消息结构311定义包含如下3个字段,及一个隐含字段:
-缓存操作消息类型字段,无符号字符型
-缓存操作消息状态字段,无符号字符型
-缓存消息长度字段,无符号短整型
-带操作的文件名字符串字段,隐含字段
缓存操作消息类型字段表示缓存操作消息的类型,包括”缓存添加操作”和”缓存删除操作”。
缓存操作消息状态字段用于表示本缓存操作消息的状态。用户向缓存系统发送缓存操作消息时,将该域设置为“待处理”状态。当缓存系统处理了该缓存操作消息后,将会设置该域。用户能够通过检查接收到的反馈消息中该域的值,判断缓存的工作情况。根据缓存操作的不同结果,该状态字段一共可以取下面这些状态值:
-缓存添加终止,文件不可以缓存
-缓存添加完成
-缓存添加完成,文件已缓存
-缓存添加终止,目标文件丢失
-缓存添加终止,找不到文件
-缓存删除完成
-缓存删除完成,备份文件丢失
-缓存删除完成,缓存文件丢失
-缓存删除完成,链接文件丢失
-缓存删除完成,链接文件、备份文件丢失
-缓存删除完成,链接文件、缓存文件丢失
-缓存删除终止,文件不可以缓存
-缓存删除终止,链接文件丢失
-缓存删除终止,常规链接文件
-缓存删除失败,备份文件、缓存文件丢失
-缓存删除失败,所有文件丢失
-未知消息类型
缓存消息长度字段用于提取该缓存消息的消息体。由于消息体的长度无法在编译时确定,因此消息体并没有采用显式的域表示,其紧跟在域缓存消息长度字段之后。因此,需要通过指针加偏移量的方式访问消息体。消息体中包含带操作的文件的完整路径名称。
2)消息队列项结构312
消息队列项结构312定义了一个链表节点,其定义包含如下2个字段:
-缓存操作消息字段,缓存操作消息结构311类型指针
-链表下一结点指针字段,消息队列项结构312类型指针
缓存操作消息字段为缓存操作消息结构311指针,将在缓存操作模块41中详细介绍。
3)消息队列最大长度变量313
消息队列最大长度变量313为静态无符号短整型,在模块初始化函数3110中完成初始化,存储了从配置文件2110中得到的消息队列最大长度。它用于限制“待处理”队列与“已处理”队列的最大长度。
4)当前“待处理”消息队列长度变量314
当前“待处理”消息队列长度变量314为静态无符号短整型,表示当前“待处理”消息队列的长度。
5)“待处理”消息队列队头指针315
“待处理”消息队列队头指针315为静态消息队列项结构312类型的指针,指向“待处理”消息队列的队头,用于对“待处理”消息队列项的访问。
6)“待处理”消息队列互斥访问锁316
“待处理”消息队列互斥访问锁316为静态pthread_mutex_t类型,它为“待处理” 消息队列提供互斥访问机制。消息队列操作函数3112、3113、3114、3115、3116首先需要获取互斥锁316,如果成功,则执行队列操作,如果失败,则等待锁。
7)当前“已处理”消息队列长度变量317
当前“已处理”消息队列长度变量317为静态无符号短整型,表示当前“已处理”队列的长度。
8)“已处理”消息队列队头指针318
“已处理”消息队列队头指针318为静态消息队列项结构312类型的指针,指向“已处理”消息队列的队头,用于对“已处理”消息队列项的访问。
9)“已处理”消息队列互斥访问锁319
“已处理”消息队列互斥访问锁319为静态pthread_mutex_t类型,它“已处理”消息队列提供互斥访问机制。消息队列操作函数3112、3113、3114、3115、3116首先需要获取互斥锁319,如果成功,则执行队列操作,如果失败,则等待锁。
10)模块初始化函数3110
模块初始化函数3110用于本模块启动时完成一系列初始化工作,该函数接收一无符号短整型参数,该参数说明消息队列最大长度,参见图6,其具体步骤如下:
-步骤31101:根据参数,设置消息队列的最大长度变量313;
-步骤31102:初始化“待处理”消息队列队头指针315;
-步骤31103:初始化“已处理”消息队列队头指针318;
-步骤31104:初始化“待处理”消息队列互斥访问锁316;
-步骤31105:初始化“已处理”消息队列互斥访问锁319。
11)模块清理函数3111
模块清理函数3111在模块退出时执行,负责清空“待处理”和“已处理”两个消息队列。
12)消息队列操作函数3112、3113、3114、3115、3116
消息队列操作函数共5个,分别是:
-消息入队函数3112
消息入队函数3112将一个缓存操作消息加入到消息队列中,该函数接收两个参数,分别是待操作队列的标识和待入队的缓存操作消息结构311指针。
-消息出队函数3113
消息出队函数3113从消息队列中取出一个缓存操作消息,该函数接收待操作 队列的标识参数,并返回取出的缓存操作消息结构311指针。
-判断队列是否空函数3114
判断队列是否空函数3114判断指定的消息队列是否为空,该函数接收待操作队列的标识参数,如果队列空,返回1,否则返回0。
-获取当前队列状态函数3115
获取当前队列状态函数3115用于获取指定的消息队列的状态,该函数接收待操作队列的标识参数,并返回当前队列长度,以及允许的最大长度。
-清空队列函数3116
清空队列函数3116清空指定的消息队列,该函数接收待操作队列的标识参数。
上述消息队列操作函数3112、3113、3114、3115、3116是消息队列管理模块31向其它模块提供的接口,它能够提供对“待处理”和“已处理”两个消息队列的互斥访问。
待操作队列的标识用于指定函数所要作用的消息队列,其可能为“待处理队列”和“已完成队列”。
4.缓存操作模块41
该模块由以下20个部分组成:
1)需缓存根路径变量411;
2)缓存根路径变量412;
3)消息循环状态变量413;
4)模块初始化函数414;
5)模块清理函数415;
6)挂载内存文件系统函数416;
7)卸载内存文件系统函数417;
8)缓存消息操作循环函数418;
9)检查文件是否可缓存函数419;
10)提取相对路径函数4110;
11)生成缓存路径函数4111;
12)检查文件是否存在函数4112;
13)检查文件是否为链接文件函数4113;
14)获取链接文件的目标文件名函数4114;
15)重命名文件函数4115;
16)创建链接文件函数4116;
17)删除文件函数4117;
18)拷贝文件函数4118;
19)添加缓存4119;
20)删除缓存4120;
各部分具体实现如下:
1)需缓存根路径变量411
需缓存根路径变量411为静态字符型指针,它在模块初始化函数414中被初始化。它保存了可缓存的根路径,所有位于根路径下的所有文件可以被缓存,其余文件不可被缓存。提取相对路径函数4110需要使用该变量提取带缓存文件相对于需缓存根路径的相对路径。
2)缓存根路径变量412
缓存根路径变量412为静态字符型指针,它在模块初始化函数414中被初始化。它保存了缓存的根路径,是内存文件系统的挂载点。生成缓存路径函数4111需要使用该变量及相对路径,生成缓存文件完整路径。
3)消息循环状态变量413
消息循环状态变量413为静态整型,用于控制缓存消息操作循环函数418的执行状态,其可能取值为“消息循环启用”和“消息循环禁用”。
4)模块初始化函数414
模块初始化函数414用于本模块启动时完成一系列初始化工作,其接收两个字符型指针参数,分别表示需缓存根路径与缓存根路径。参见图7,其具体步骤如下:
-步骤4141:用参数初始化需缓存根路径变量411和缓存根路径变量412;
-步骤4142:调用函数416挂载内存文件系统到缓存根路径变量412表示的路径下;
-步骤4143:设置消息循环状态变量413为“缓存操作消息循环启用”;
-步骤4144:创建子线程2;
-步骤4145:在子线程2中开始执行缓存消息操作循环函数418。
5)模块清理函数415
模块清理函数415在模块退出时完成一系列清理工作,参见图8,其具体步骤如下:
-步骤4151:设置消息循环状态变量413为“缓存操作消息循环禁用”;
-步骤4152:等待子线程2结束;
-步骤4153:调用函数417卸载缓存根路径变量412表示的路径下的内存文件系统;
-步骤4154:释放需缓存根路径变量411和缓存根路径变量412的内存空间。
6)挂载内存文件系统函数416
内存文件系统是使用本发明必须使用的关键技术,它是缓存文件的存储位置。本发明可以使用多种常见的内存文件系统。本实施示例使用Tmpfs。
挂载内存文件系统函数416接收一个字符型指针参数,表示缓存根路径。该函数在模块初始化函数414中被调用,用于挂载内存文件系统Tmpfs到缓存根路径参数表示的路径下。函数416中,使用操作系统的system()系统调用执行内存文件系统挂载命令,例如:mount tmpfs/cache/-t tmpfs-o size=128m。
当使用其它内存文件系统时,需要将system()系统调用运行的命令做相应的替换。
7)卸载内存文件系统函数417
卸载内存文件系统函数417接收一个字符型指针参数,表示缓存根路径。该函数在模块清理函数415中被调用,用于卸载已经挂载到缓存根路径参数表示的路径下的内存文件系统。
8)缓存消息操作循环函数418
缓存消息操作循环函数418在子线程2中开始启动,负责执行缓存消息中指定的缓存操作,参见图9,其具体步骤如下:
-步骤4181:开始无限循环;
-步骤4182:检查消息循环状态变量413,如果为“缓存操作消息循环禁用”,则转向步骤41812,否则继续步骤4183;
-步骤4183:调用消息队列管理模块31的消息出队函数3113从“待处理”消息队列中取出一个缓存操作消息;
-步骤4184:如果成功获取缓存消息,则转向步骤4186;否则继续步骤4185;
-步骤4185:等待10毫秒,然后转向步骤4183;
-步骤4186:判断取出的缓存操作消息类型:
■如果类型为”缓存删除操作”,则是缓存文件删除消息,转向步骤4187;
■如果类型为”缓存添加操作”,则是缓存文件添加消息,转向步骤4188;
■其他情况,转向步骤41810;
-步骤4187:调用缓存删除函数4120,转向步骤4189;
-步骤4188:调用缓存添加函数4119,转向步骤4189;
-步骤4189:将函数返回值存入该缓存消息的状态域中,转向步骤41811;
-步骤41810:设置该缓存消息的状态域为“未知消息类型”;
-步骤41811:调用消息队列管理模块31的消息入队函数3112将该缓存消息加入“已处理”消息队列中,转向步骤4182;
-步骤41812:结束循环。
9)检查文件是否可缓存函数419
检查文件是否可缓存函数419接收一个字符型指针参数,表示待检查的文件名。函数通过将参数和需缓存根路径变量411表示的字符串进行比较,从而检查参数表示的文件是否位于系统的可缓存路径下。
如果是,则返回1,表示可以缓存该文件;否则,返回0,表示不可以缓存该文件。
10)提取相对路径函数4110
提取相对路径函数4110接收一个字符型指针参数,表示文件的完整路径名。函数通过在参数表示的完整路径名中截取除去需缓存根路径变量411表示的字符串的一个子串,作为待缓存对象的相对路径名,并返回该路径名字符串的指针。
例如,参数为”/var/www/html/index.html”,需缓存根路径变量411为”/var/www/html/”,则函数4110将返回”index.html”。
11)生成缓存路径函数4111
生成缓存路径函数4111接收一个字符型指针参数,表示文件的相对路径名。函数通过将参数表示的相对路径名与缓存根路径变量412表示的字符串进行拼接,生成完整的缓存文件路径名,并返回该路径名字符串的指针。
例如,参数为”index.html”,缓存根路径变量412为”/cache/”,则函数4111将返回”/cache/index.html”。
12)检查文件是否存在函数4112
检查文件是否存在函数4112接收一个字符型指针参数,表示待检查的文件名。函数通过系统调用access(const char *pathname,int mode)检查参数表示的文件是否存在。
如果是,返回1;否则返回0。
13)检查文件是否为链接文件函数4113
检查文件是否为链接文件函数4113接收一个字符型指针参数,表示待检查的文件名。函数通过系统调用lstat(const char *restrict pathname,struct stat*restrict buf)及系统宏S_ISLNK(),检查参数表示的文件是否存在。
如果是,返回1;否则返回0。
14)获取链接文件的目标文件名函数4114
获取链接文件的目标文件名函数4114接收一个字符型指针参数,表示待操作的链接文件名。函数通过系统调用readlink(const char *restrict pathname,char*restrict buf,size_t bufsize)获取链接文件参数指向的真实文件的完整路径名。
如果成功,返回指向获取的路径名字符指针;否则,返回NULL。
15)重命名文件函数4115
重命名文件函数4115接收两个字符型指针参数,分别表示旧文件名与新文件名。函数通过系统调用rename(const char *oldname,const char *newname)将旧文件名参数表示的文件重命名为新文件名参数表示的文件名。
如果操作成功,返回1;否则返回0。
16)创建链接文件函数4116
创建链接文件函数4116接收两个字符型指针参数,分别表示待创建的链接文件名与链接文件将指向的目标文件名。函数通过系统调用symlink(const char*actualpath,const char *sympath),创建由链接文件名参数指定的符号链接文件,该链接文件指向由目标文件名参数指定的文件。
如果操作成功,返回1;否则返回0。
17)删除文件函数4117
删除文件函数4117接收一个字符型指针参数,表示待删除的文件名。函数通过系统调用remove(const char *pathname)删除参数表示的文件。
如果操作成功,返回1;否则返回0。
18)拷贝文件函数4118
拷贝文件函数4118接收两个字符型指针参数,分别表示待拷贝的源文件名与目标文件名。函数通过系统调用system(const char *comstring)调用命令行cp命令,将参数表示的源文件拷贝生成参数表示的目标文件。
如果操作成功,返回1;否则返回0。
19)添加缓存函数4119
添加缓存函数4119接收一个字符型指针参数,表示待添加的缓存文件。参见图11,具体流程如下。为了步骤的叙述简便,用src_name表示待添加缓存的文件名,用cache_name表示存储在缓存中的缓存文件完整路径名,用ret表示用于保存需要返回的状态的变量。
-步骤41191:调用函数419,检查待缓存文件是否位于需缓存根路径下,如果是,继续步骤41192;否则转到步骤41193;
-步骤41192:设置状态变量ret为“缓存添加终止,文件不可以缓存”,然后转到步骤411920;
-步骤41193:调用函数4110,提取待缓存文件的相对路径名,存到变量name中;
-步骤41194:调用函数4111,生成缓存文件完整路径名,存到变量cache_name中;
-步骤41195:调用函数4112,检查是否存在文件src_name,如果是,继续步骤41196;否则,转向步骤411919;
-步骤41196:调用函数4113,检查文件src_name是否为链接文件,如果不是,继续步骤41197;否则,转向步骤411911;
-步骤41197:调用函数4118,拷贝文件src_name到文件cache_name;
-步骤41198:调用函数4115,将文件src_name重命名为src_name.aclm;
-步骤41199:调用函数4116,创建链接文件src_name,指向文件cache_name;
-步骤411910:设置状态变量ret为“缓存添加完成”,然后转到步骤411920;
-步骤411911:调用函数4114,检查src_name是否指向cache_name,如果是,继续步骤411912;否则,转向步骤41198;
-步骤411912:调用函数4112,检查是否存在文件cache_name,如果是,继续步骤 411913;否则,转向步骤411914;
-步骤411913:设置状态变量ret为“缓存添加完成,文件已缓存”,然后转到步骤 411920;
-步骤411914:调用函数4112,检查是否存在文件src_name.aclm,如果是,继续步骤411915;否则,转向步骤411917;
-步骤411915:调用函数4118,拷贝文件src_name.aclm到文件cache_name;
-步骤411916:设置状态变量ret为“缓存添加完成”,然后转到步骤411920;
-步骤411917:调用函数4117,删除失效的链接文件src_name;
-步骤411918:设置状态变量ret为“缓存添加终止,目标文件丢失”,然后转到步骤411920;
-步骤411919:设置状态变量ret为“缓存添加终止,找不到文件”,然后转到步骤 411920
-步骤411920:本函数结束,返回状态变量ret。
20)删除缓存函数4120
删除缓存函数4120接收一个字符型指针参数,表示待删除的缓存文件。参见图11,具体流程如下。为了步骤的叙述简便,用src_name表示待删除缓存的文件名,用cache_name表示存储在缓存中的缓存文件完整路径名,用ret表示用于保存需要返回的状态的变量。
-步骤41201:调用函数419,检查文件src_name是否位于需缓存根路径下,如果是,继续步骤41202;否则转到步骤412015;
-步骤41202:调用函数4110,提取待缓存文件的相对路径名,存到变量name中;
-步骤41203:调用函数4111,生成缓存文件完整路径名cache_name;
-步骤41204:调用函数4112,检查是否存在文件src_name,如果是,继续步骤41205;否则,转向步骤412023;
-步骤41205:调用函数4113,检查文件src_name是否为链接文件,如果是,继续步骤41206;否则,转向步骤412016;
-步骤41206:调用函数4114,检查文件src_name是否指向文件cache_name,如果是,继续步骤41207;否则,转向步骤412017;
-步骤41207:调用函数4112,检查是否存在文件cache_name,如果是,继续步骤 41208;否则,转向步骤412018;
-步骤41208:调用函数4117,删除文件src_name;
-步骤41209:调用函数4118,拷贝文件cache_name到文件src_name;
-步骤412010:调用函数4117,删除文件cache_name;
-步骤412011:调用函数4112,检查是否存在文件src_name.aclm,如果是,继续步骤412012;否则,转向步骤412014;
-步骤412012:调用函数4117,删除文件src_name.aclm;
-步骤412013:设置状态变量ret为“缓存删除完成”,然后转到步骤412034;
-步骤412014:设置状态变量ret为“缓存删除完成,备份文件丢失”,然后转到步骤412034;
-步骤412015:设置状态变量ret为“缓存删除终止,文件不可以缓存”,然后转到步骤412034;
-步骤412016:设置状态变量ret为“缓存删除终止,链接文件丢失”,然后转到步骤412034;
-步骤412017:设置状态变量ret为“缓存删除终止,常规链接文件”,然后转到步骤412034;
-步骤412018:调用函数4112,检查是否存在文件src_name.aclm,如果是,继续步骤412019;否则,转向步骤412023;
-步骤412019:调用函数4117,删除文件src_name;
-步骤412020:调用函数4118,拷贝文件src_name.aclm到文件src_name;
-步骤412021:调用函数4117,删除文件src_name.aclm;
-步骤412022:设置状态变量ret为“缓存删除完成,缓存文件丢失”,然后转到步骤412034;
-步骤412023:设置状态变量ret为“缓存删除失败,备份文件、缓存文件丢失”,然后转到步骤412034;
-步骤412024:调用函数4112,检查是否存在文件cache_name,如果是,继续步骤 412025;否则,转向步骤412030;
-步骤412025:调用函数4118,拷贝文件cache_name到文件src_name;
-步骤412026:调用函数4112,检查是否存在文件src_name.aclm,如果是,继续步骤412027;否则,转向步骤412029;
-步骤412027:调用函数4117,删除文件src_name.aclm;
-步骤412028:设置状态变量ret为“缓存删除完成,链接文件丢失”,然后转到步骤412034;
-步骤412029:设置状态变量ret为“缓存删除完成,链接文件、备份文件丢失”,然后转到步骤412034;
-步骤412030:调用函数4112,检查是否存在文件src_name.aclm,如果是,继续步骤412031;否则,转向步骤412033;
-步骤412031:调用函数4118,拷贝文件src_name.aclm到文件src_name;
-步骤412032:设置状态变量ret为“缓存删除完成,链接文件、缓存文件丢失”,然后转到步骤412034;
-步骤412033:设置状态变量ret为“缓存删除失败,所有文件丢失”,然后转到步骤412034;
-步骤412034:结束缓存从删除流程,返回状态变量ret。
5.通信模块51
该模块由以下8个部分组成:
1)消息队列状态通告结构511;
2)监听端口号变量512;
3)客户端地址结构变量513;
4)消息循环状态变量514;
5)监听套接字变量515;
6)客户套接字变量516;
7)模块初始化函数517;
8)模块清理函数518;
9)消息循环函数519;
10)队列状态通告发送函数5110;
11)缓存反馈消息发送函数5111。
各部分具体实现如下:
1)消息队列状态通告结构511
消息队列状态通告结构511定义包含如下两个字段:
-消息队列待处理消息数量字段,无符号短整形
-消息队列剩余大小字段,无符号短整型
结构511定义了消息队列的状态通告结构。其中,消息队列待处理消息数量字段表示当前消息队列中待处理的消息数量,消息队列剩余大小字段表示当前消息队列中还可以容纳的消息数量。
通信模块51将在“待处理”消息队列长度314分别达到消息队列最大长度313的80%,85%,90%,95%时,向用户发送消息队列状态通告511。当“待处理”消息队列长度314达到消息队列最大长度313时,通信模块51将丢弃该消息,并向用户发送队列已满的消息队列状态通告511。
2)监听端口号变量512
监听端口号变量512为静态无符号短整型变量,在模块初始化函数517中完成初始化,用于保存通信模块51监听用户连接的端口号。
3)客户端地址结构变量513
客户端地址结构变量513为静态sockaddr_in类型变量,在模块初始化函数517中被赋值,用于保存当前连接到系统的用户的地址信息。该地址信息在缓存反馈消息发送函数5111中需要使用。
4)消息循环状态变量514
消息循环状态变量514为静态整型变量,用于控制消息循环函数519的执行状态,其可能取值为“消息循环启用”和“消息循环禁用”。
5)监听套接字变量515
监听套接字变量515为静态socket类型变量,在模块初始化函数517中初始化,用于监听来自用户的连接。
6)客户套接字变量516
客户套接字变量516为静态socket类型变量,在模块初始化函数517中初始化,在和用户建立连接后进行数据收发。
7)模块初始化函数517
模块初始化函数517用于本模块启动时完成一系列初始化工作。函数接收一个无符号短整型参数,用于指定监听的端口号。参见图12,其具体步骤如下:
-步骤5171:根据参数,设置监听端口号变量512;
-步骤5172:设置消息循环状态变量514为“消息循环启动”;
-步骤5173:调用socket创建监听套接字515;
-步骤5174:调用bind将套接字515与监听端口号变量512指定的端口绑定;
-步骤5175:调用listen启动监听套接字515监听;
-步骤5176:调用accept等待客户端连接;
-步骤5177:将accept返回的socket实例保存到客户套接字516中;
-步骤5178:创建子线程1;
-步骤5179:在子线程1中执行消息循环函数519开始消息循环。
8)模块清理函数518
模块清理函数518在模块退出时完成一系列清理工作。参见图13,其具体步骤如 下:
-步骤5181:设置消息循环状态变量514为“消息循环启用”;
-步骤5182:等待子线程1结束;
-步骤5183:关闭客户套接字516;
-步骤5184:关闭监听套接字515。
9)消息循环函数519
消息循环函数519在子线程1中开始启动,负责通信模块的消息接收、发送,参见图14,其具体步骤如下:
-步骤5191:调用消息队列管理模块31的返回当前队列状态函数3115,获取当前队列的长度及最大运行的队列长度,并保存在临时变量current和max中;
-步骤5192:开始无限循环;
-步骤5193:检查消息循环状态变量514,如果为“消息循环禁用”,则转向步骤 51912,否则继续步骤5194;
-步骤5194:调用recv从客户套接字516中接收数据;
-步骤5195:根据current和max计算队列的使用情况,并做如下判断:
■如果队列使用达到100%,转向步骤5196;
■如果队列使用达到80%,85%,90%,95%,转向步骤5198;
■其他情况,继续步骤5199;
-步骤5196:丢弃本次接收到的消息;
-步骤5197:构造队列状态通告消息511,调用队列状态通告发送函数5110向用户发送该通告消息,然后转向步骤51910;
-步骤5198:构造队列状态通告消息511,调用队列状态通告发送函数5110向用户发送该通告消息;
-步骤5199:调用消息队列管理模块31的消息入队函数3112将接收到的消息加入“待处理”消息队列中;
-步骤51910:重新调用消息队列管理模块31的返回当前队列状态函数3115,获取当前队列的长度及最大运行的队列长度,并更新临时变量current和max;
-步骤51911:调用缓存反馈消息发送函数5111发送反馈消息,然后转向步骤5193;
-步骤51912:结束循环。
10)队列状态通告发送函数5110
队列状态通告发送函数5110接收一个消息队列状态通告结构511类型的参数,调用send函数,将参数指向的队列状态通告511发送给连接到系统的用户。
11)缓存反馈消息发送函数5111
缓存反馈消息发送函数5111负责向用户发送缓存反馈消息,参见图15,具体步骤如下:
-步骤51111:调用消息队列管理模块31的返回当前队列状态函数3115;
-步骤51112:将返回的队列状态中的当前队列长度的一半,作为需要出队的消息数量N;
-步骤51113:调用消息队列管理模块31的消息出队函数3113,从“已处理”消息队列中取出一个缓存消息;
-步骤51114:调用缓存反馈消息发送函数5111,将取出缓存消息依次发送给客户;
-步骤51115:将N减1;
-步骤51116:如果N大于0,则转向步骤51113;否则结束本函数。