进程间信号通信,进程 通信
对CD关键字的搜索是复杂的。该函数的用户希望在调用后立即开始搜索。我们在第7章中通过在第一次调用时将*first_call_ptr设置为true来满足这个要求,函数返回第一个匹配结果。在下一次搜索函数调用中,*first_call_ptr设置为false,将返回更多匹配,每次调用都将返回一个匹配结果。
现在我们已经将程序分成了两个进程,我们不能再允许搜索在服务器端一次处理一个实体,因为另一个不同的客户端可能会向服务器请求不同的搜索,而我们的搜索仍在处理中。我们不能让服务器为每个客户端存储搜索内容(搜索到的地方),因为当用户找到他们正在寻找的CD的键值或者客户端有问题时,客户端可以简单地停止搜索。
我们可以改变执行搜索的方式,或者像我们在这里选择的那样,隐藏接口例程中的复杂性。我们所做的是让服务器返回所有可能的搜索匹配,然后将它们存储在一个临时文件中,直到客户端请求它们。
测试-查找
1这个函数看起来更复杂,因为它调用了我们将在下一节讨论的三个管道函数:send _ mess _ to _ server、start _ resp _ from _ server和read_resp_from_server。
CDC _ entry search _ CDC _ entry(const char * CD _ catalog _ ptr,int *first_call_ptr)
{
message _ db _ t mess _ send
message _ db _ t mess _ ret
静态文件* work _ FILE=(FILE *)0;
静态int entries _ matching=0;
cdc _ entry ret _ val
ret _ val . catalog[0]=/0 ;
如果(!work _ file(* first _ call _ ptr==0))ret _ val;
2这里是搜索的第一个调用,其中*first_call_ptr设置为true。万一我们忘记了,立即将其设置为false。在此函数中创建一个work_file,并初始化客户端消息结构。
if (*first_call_ptr) {
* first _ call _ ptr=0;
if(work _ file)fclose(work _ file);
work _ file=tmpfile();
如果(!work _ file)return(ret _ val);
mess _ send.client _ pid=mypid
mess _ send . request=s _ find _ CDC _ entry;
strcpy(mess _ send . CDC _ entry _ data . catalog,CD _ catalog _ ptr);
下一步是三层条件测试。如果消息成功发送到服务器,客户端将等待服务器的响应。当服务器的读取操作成功时,搜索匹配项会被记录在work_file中,相应的entries_matching也会增加。
if(send _ mess _ to _ server(mess _ send))。
if (start_resp_from_server()) {
while(read _ resp _ from _ server(mess _ ret)){
if(mess _ ret . response==r _ success){
fwrite( mess_ret.cdc_entry_data,sizeof(cdc_entry),1,work _ file);
条目_匹配;
}否则{
打破;
}
} /*当*/
}否则{
fprintf(stderr,"服务器没有响应/n ");
}
}否则{
fprintf(stderr,"服务器不接受请求/n ");
}
4以下测试检查搜索是否成功。然后,fseek调用会将work_file设置为下一个将写入数据的位置。
if (entries_matching==0) {
fclose(work _ file);
work _ FILE=(FILE *)0;
ret(ret _ val);
}
(void)fseek(work_file,0L,SEEK _ SET);
5如果这不是第一次使用特定的搜索模式调用搜索函数,下面的代码将检查是否有任何剩余的匹配。最后,下一个匹配将被读入ret_val结构。之前的测试发现有匹配。
}否则{
/* not *first_call_ptr */
if (entries_matching==0) {
fclose(work _ file);
work _ FILE=(FILE *)0;
ret(ret _ val);
}
}
fread( ret_val,sizeof(cdc_entry),1,work _ file);
条目_匹配—;
ret(ret _ val);
}
服务器接口
就像客户端有一个app_ui.c程序的接口一样,服务器也需要一个程序来控制(改名后的)cd_access.c,也就是现在的cd_dbm.c服务器的主要功能如下。
实验-服务器. c
1我们在程序开始时声明了几个全局变量,一个process_command函数的原型,以及一个信号捕获函数以确保干净的退出。
#包括unistd.h
#包含stdlib.h
#包含stdio.h
#包含fcntl.h
#包括限额. h
#包含信号. h
#包含字符串. h
#包含错误号h
#包含sys/types.h
#包含系统/统计信息
#include "cd_data.h "
#包含" cliserv.h "
int保存_错误
static int server _ running=1;
静态void process _ command(常量message _ db _ t mess _ command);
void catch_signals()
{
server _ running=0;
}
2现在我们来了解一下主要的函数。在检测信号捕获例程正确之后,程序检测我们是否在命令行传递了一个[构成来自拉丁语、结尾为-我们的名词的复数]选项。如果我们传递了,程序就会创建一个新的数据库。如果cd_dbm.c中的数据库_初始化例程失败,就会显示一个错误消息。如果一切正常而且服务器正在运行,由客户端来的请求就会被传递给过程命令函数,这个函数我们将会稍后介绍。
int main(int argc,char *argv[]) {
结构信号动作新动作,旧动作
消息_数据库_测试消息_命令
int database _ init _ type=0;
新动作。sa _ handler=catch _ signals
sigemptyset(new _ action。sa _ mask);
新动作。sa _ flags=0;
if ((sigaction(SIGINT,new_action,old_action)!=0)
(sigaction(SIGHUP,new_action,old_action)!=0)
(sigaction(SIGTERM,new_action,old_action)!=0)) {
fprintf(stderr,"服务器启动错误,信号捕捉失败/n ");
退出(退出_失败);
}
if (argc 1) {
参数
if (strncmp("-i ",*argv,2)==0)database _ init _ type=1;
}
如果(!数据库初始化(数据库初始化类型)){
fprintf(stderr,"服务器错误:-/
无法初始化数据库/n ");
退出(退出_失败);
}
如果(!server_starting())退出(退出_失败);
while(server_running) {
if(read _ request _ from _ client(mess _ command))。
process _ command(mess _ command);
}否则{
if(Server _ running)fprintf(stderr,"服务器结束-无法/
读管道/n ");
server _ running=0;
}
} /*当*/
server _ ending();
退出(退出_成功);
}
3所有的客户端消息都会被传递给过程命令函数,在那里他们会被传递给一个情况语句,从而执行cd_dbm.c中的正确调用。
静态void process _ command(const message _ db _ t comm)
{
消息_数据库_测试响应;
int first _ time=1;
resp=comm/*复制命令,然后根据需要更改响应*/
如果(!启动响应到客户端(响应)){
fprintf(stderr,"服务器警告:-/
开始响应客户端%d失败/n ",分别为。client _ PID);
返回;
}
resp.response=r _成功
memset(resp.error_text,/0 ,sizeof(resp。error _ text));
save _ errno=0;
开关(响应请求){
案例s _创建_新建_数据库:
如果(!database _ initialize(1))resp。响应=r _失败;
打破;
案例s_get_cdc_entry:
resp.cdc_entry_data=
get _ CDC _ entry(comm . CDC _ entry _ data。目录);
打破;
案例s_get_cdt_entry:
resp.cdt_entry_data=
get _ CDT _ entry(comm . CDT _ entry _ data。目录,
通信CDT _条目_数据。track _ no);
打破;
案例s_add_cdc_entry:
如果(!add _ CDC _ entry(comm . CDC _ entry _ data))resp。响应=
r _失败;
打破;
案例s_add_cdt_entry:
如果(!add _ CDT _ entry(comm . CDT _ entry _ data))resp。响应=
r _失败;
打破;
案例s_del_cdc_entry:
如果(!del _ CDC _ entry(comm . CDC _ entry _ data。目录)。反应
=r _失败;
打破;
案例s_del_cdt_entry:
如果(!del _ CDT _ entry(comm . CDT _ entry _ data。目录,
通信CDT _条目_数据。track _ no))resp。响应=r _失败;
打破;
案例s_find_cdc_entry:
做{
resp.cdc_entry_data=
search _ CDC _ entry(comm . CDC _ entry _ data。目录
第一次);
如果(resp。CDC _ entry _ data。目录[0]!=0) {
resp.response=r _成功
如果(!发送响应至客户端(响应))
fprintf(stderr,"服务器警告:-/
未能响应%d/n”,分别为。client _ PID);
打破;
}
}否则{
resp.response=r _ find _ no _ more
}
} while(resp。响应==r _ success);
打破;
默认值:
resp.response=r _失败
打破;
} /*开关*/
sprintf(resp.error_text,"命令失败:/n/t%s/n ",
strerror(save _ errno));
如果(!发送响应至客户端(响应))
fprintf(stderr,"服务器警告:-/
未能响应%d/n”,分别为。client _ PID);
}
end _ resp _ to _ client();
返回;
}
在我们了解实际的管道实现之前,让我们讨论一下在客户端和服务器进程之间传输数据需要发生的事件序列。图13-9显示了启动的客户端和服务器进程,以及它们在处理命令和响应时是如何循环的。
在这个实现中,情况有些困难,因为对于一个搜索请求,客户机向服务器发送一个命令,然后期望服务器接收一个或多个响应。这将导致一些额外的复杂性,主要是在客户端。